myoperator-mcp 0.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.
package/dist/index.js ADDED
@@ -0,0 +1,1303 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+
6
+ // src/server/index.ts
7
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
+
9
+ // src/data/metadata.ts
10
+ var componentMetadata = {
11
+ button: {
12
+ name: "Button",
13
+ description: "A customizable button component with variants, sizes, and icons. Supports loading states and can render as a child element using Radix Slot.",
14
+ dependencies: [
15
+ "@radix-ui/react-slot",
16
+ "class-variance-authority",
17
+ "clsx",
18
+ "tailwind-merge",
19
+ "lucide-react"
20
+ ],
21
+ props: [
22
+ {
23
+ name: "variant",
24
+ type: '"default" | "destructive" | "outline" | "secondary" | "ghost" | "link"',
25
+ required: false,
26
+ description: "The visual style of the button",
27
+ defaultValue: "default"
28
+ },
29
+ {
30
+ name: "size",
31
+ type: '"default" | "sm" | "lg" | "icon"',
32
+ required: false,
33
+ description: "The size of the button",
34
+ defaultValue: "default"
35
+ },
36
+ {
37
+ name: "asChild",
38
+ type: "boolean",
39
+ required: false,
40
+ description: "Render as child element using Radix Slot",
41
+ defaultValue: "false"
42
+ },
43
+ {
44
+ name: "leftIcon",
45
+ type: "React.ReactNode",
46
+ required: false,
47
+ description: "Icon displayed on the left side of the button text"
48
+ },
49
+ {
50
+ name: "rightIcon",
51
+ type: "React.ReactNode",
52
+ required: false,
53
+ description: "Icon displayed on the right side of the button text"
54
+ },
55
+ {
56
+ name: "loading",
57
+ type: "boolean",
58
+ required: false,
59
+ description: "Shows loading spinner and disables button",
60
+ defaultValue: "false"
61
+ },
62
+ {
63
+ name: "loadingText",
64
+ type: "string",
65
+ required: false,
66
+ description: "Text shown during loading state"
67
+ },
68
+ {
69
+ name: "disabled",
70
+ type: "boolean",
71
+ required: false,
72
+ description: "Disables the button",
73
+ defaultValue: "false"
74
+ }
75
+ ],
76
+ variants: [
77
+ {
78
+ name: "variant",
79
+ options: [
80
+ "default",
81
+ "destructive",
82
+ "outline",
83
+ "secondary",
84
+ "ghost",
85
+ "link"
86
+ ],
87
+ defaultValue: "default"
88
+ },
89
+ {
90
+ name: "size",
91
+ options: ["default", "sm", "lg", "icon"],
92
+ defaultValue: "default"
93
+ }
94
+ ],
95
+ examples: [
96
+ {
97
+ title: "Basic Button",
98
+ code: "<Button>Click me</Button>",
99
+ description: "Simple button with default styling"
100
+ },
101
+ {
102
+ title: "Button Variants",
103
+ code: `<Button variant="default">Default</Button>
104
+ <Button variant="secondary">Secondary</Button>
105
+ <Button variant="outline">Outline</Button>
106
+ <Button variant="ghost">Ghost</Button>
107
+ <Button variant="link">Link</Button>
108
+ <Button variant="destructive">Destructive</Button>`,
109
+ description: "All available button variants"
110
+ },
111
+ {
112
+ title: "Button with Icons",
113
+ code: `import { Mail, ArrowRight } from "lucide-react"
114
+
115
+ <Button leftIcon={<Mail />}>Send Email</Button>
116
+ <Button rightIcon={<ArrowRight />}>Next</Button>`,
117
+ description: "Buttons with left or right icons"
118
+ },
119
+ {
120
+ title: "Loading State",
121
+ code: `<Button loading>Loading</Button>
122
+ <Button loading loadingText="Saving...">Save</Button>`,
123
+ description: "Button with loading spinner"
124
+ },
125
+ {
126
+ title: "Icon Only Button",
127
+ code: `import { Plus } from "lucide-react"
128
+
129
+ <Button size="icon" aria-label="Add item">
130
+ <Plus />
131
+ </Button>`,
132
+ description: "Square icon-only button"
133
+ }
134
+ ]
135
+ },
136
+ badge: {
137
+ name: "Badge",
138
+ description: "A status badge component with active, failed, and disabled variants. Pill-shaped badges with different colors for different states.",
139
+ dependencies: ["class-variance-authority", "clsx", "tailwind-merge"],
140
+ props: [
141
+ {
142
+ name: "variant",
143
+ type: '"active" | "failed" | "disabled" | "default"',
144
+ required: false,
145
+ description: "The visual style of the badge",
146
+ defaultValue: "default"
147
+ },
148
+ {
149
+ name: "size",
150
+ type: '"default" | "sm" | "lg"',
151
+ required: false,
152
+ description: "The size of the badge",
153
+ defaultValue: "default"
154
+ },
155
+ {
156
+ name: "leftIcon",
157
+ type: "React.ReactNode",
158
+ required: false,
159
+ description: "Icon displayed on the left side of the badge text"
160
+ },
161
+ {
162
+ name: "rightIcon",
163
+ type: "React.ReactNode",
164
+ required: false,
165
+ description: "Icon displayed on the right side of the badge text"
166
+ }
167
+ ],
168
+ variants: [
169
+ {
170
+ name: "variant",
171
+ options: ["active", "failed", "disabled", "default"],
172
+ defaultValue: "default"
173
+ },
174
+ {
175
+ name: "size",
176
+ options: ["default", "sm", "lg"],
177
+ defaultValue: "default"
178
+ }
179
+ ],
180
+ examples: [
181
+ {
182
+ title: "Status Badges",
183
+ code: `<Badge variant="active">Active</Badge>
184
+ <Badge variant="failed">Failed</Badge>
185
+ <Badge variant="disabled">Disabled</Badge>
186
+ <Badge variant="default">Default</Badge>`,
187
+ description: "Badges for different status states"
188
+ },
189
+ {
190
+ title: "Badge with Icons",
191
+ code: `import { Check, X } from "lucide-react"
192
+
193
+ <Badge variant="active" leftIcon={<Check />}>Success</Badge>
194
+ <Badge variant="failed" leftIcon={<X />}>Error</Badge>`,
195
+ description: "Badges with status icons"
196
+ },
197
+ {
198
+ title: "Badge Sizes",
199
+ code: `<Badge size="sm">Small</Badge>
200
+ <Badge size="default">Default</Badge>
201
+ <Badge size="lg">Large</Badge>`,
202
+ description: "Different badge sizes"
203
+ }
204
+ ]
205
+ },
206
+ tag: {
207
+ name: "Tag",
208
+ description: "A tag component for event labels with optional bold label prefix. Rounded rectangle tags for categorization.",
209
+ dependencies: ["class-variance-authority", "clsx", "tailwind-merge"],
210
+ props: [
211
+ {
212
+ name: "variant",
213
+ type: '"default" | "primary" | "secondary" | "success" | "warning" | "error"',
214
+ required: false,
215
+ description: "The visual style of the tag",
216
+ defaultValue: "default"
217
+ },
218
+ {
219
+ name: "size",
220
+ type: '"default" | "sm" | "lg"',
221
+ required: false,
222
+ description: "The size of the tag",
223
+ defaultValue: "default"
224
+ },
225
+ {
226
+ name: "label",
227
+ type: "string",
228
+ required: false,
229
+ description: "Bold label prefix displayed before the content"
230
+ },
231
+ {
232
+ name: "interactive",
233
+ type: "boolean",
234
+ required: false,
235
+ description: "Make the tag clickable with hover/active states",
236
+ defaultValue: "false"
237
+ },
238
+ {
239
+ name: "selected",
240
+ type: "boolean",
241
+ required: false,
242
+ description: "Show selected state with ring outline",
243
+ defaultValue: "false"
244
+ }
245
+ ],
246
+ variants: [
247
+ {
248
+ name: "variant",
249
+ options: ["default", "primary", "secondary", "success", "warning", "error"],
250
+ defaultValue: "default"
251
+ },
252
+ {
253
+ name: "size",
254
+ options: ["default", "sm", "lg"],
255
+ defaultValue: "default"
256
+ }
257
+ ],
258
+ examples: [
259
+ {
260
+ title: "Basic Tags",
261
+ code: `<Tag>After Call Event</Tag>
262
+ <Tag variant="primary">Primary</Tag>
263
+ <Tag variant="success">Success</Tag>`,
264
+ description: "Simple tag labels"
265
+ },
266
+ {
267
+ title: "Tag with Label Prefix",
268
+ code: `<Tag label="In Call Event:">Start of call, Bridge, Call ended</Tag>
269
+ <Tag label="Category:">Marketing</Tag>`,
270
+ description: "Tags with bold label prefix"
271
+ },
272
+ {
273
+ title: "Interactive Tags",
274
+ code: `<Tag interactive onClick={() => console.log('clicked')}>
275
+ Clickable
276
+ </Tag>
277
+ <Tag interactive selected>Selected Tag</Tag>`,
278
+ description: "Clickable and selectable tags"
279
+ }
280
+ ]
281
+ },
282
+ table: {
283
+ name: "Table",
284
+ description: "A composable table component with size variants, loading/empty states, sticky columns, and sorting support.",
285
+ dependencies: ["class-variance-authority", "clsx", "tailwind-merge"],
286
+ props: [
287
+ {
288
+ name: "size",
289
+ type: '"sm" | "md" | "lg"',
290
+ required: false,
291
+ description: "The row height of the table",
292
+ defaultValue: "md"
293
+ },
294
+ {
295
+ name: "withoutBorder",
296
+ type: "boolean",
297
+ required: false,
298
+ description: "Remove outer border from the table",
299
+ defaultValue: "false"
300
+ }
301
+ ],
302
+ variants: [
303
+ {
304
+ name: "size",
305
+ options: ["sm", "md", "lg"],
306
+ defaultValue: "md"
307
+ }
308
+ ],
309
+ examples: [
310
+ {
311
+ title: "Basic Table",
312
+ code: `<Table>
313
+ <TableHeader>
314
+ <TableRow>
315
+ <TableHead>Name</TableHead>
316
+ <TableHead>Status</TableHead>
317
+ <TableHead>Email</TableHead>
318
+ </TableRow>
319
+ </TableHeader>
320
+ <TableBody>
321
+ <TableRow>
322
+ <TableCell>John Doe</TableCell>
323
+ <TableCell><Badge variant="active">Active</Badge></TableCell>
324
+ <TableCell>john@example.com</TableCell>
325
+ </TableRow>
326
+ </TableBody>
327
+ </Table>`,
328
+ description: "Simple table with header and body"
329
+ },
330
+ {
331
+ title: "Table with Loading State",
332
+ code: `<Table>
333
+ <TableHeader>
334
+ <TableRow>
335
+ <TableHead>Name</TableHead>
336
+ <TableHead>Status</TableHead>
337
+ </TableRow>
338
+ </TableHeader>
339
+ <TableBody>
340
+ <TableSkeleton rows={5} columns={2} />
341
+ </TableBody>
342
+ </Table>`,
343
+ description: "Table showing loading skeleton"
344
+ },
345
+ {
346
+ title: "Table with Empty State",
347
+ code: `<Table>
348
+ <TableHeader>
349
+ <TableRow>
350
+ <TableHead>Name</TableHead>
351
+ <TableHead>Status</TableHead>
352
+ </TableRow>
353
+ </TableHeader>
354
+ <TableBody>
355
+ <TableEmpty colSpan={2}>No results found</TableEmpty>
356
+ </TableBody>
357
+ </Table>`,
358
+ description: "Table showing empty state message"
359
+ },
360
+ {
361
+ title: "Table with Sticky Column",
362
+ code: `<Table>
363
+ <TableHeader>
364
+ <TableRow>
365
+ <TableHead sticky>ID</TableHead>
366
+ <TableHead>Name</TableHead>
367
+ <TableHead>Description</TableHead>
368
+ </TableRow>
369
+ </TableHeader>
370
+ <TableBody>
371
+ <TableRow>
372
+ <TableCell sticky>001</TableCell>
373
+ <TableCell>Item Name</TableCell>
374
+ <TableCell>Long description text...</TableCell>
375
+ </TableRow>
376
+ </TableBody>
377
+ </Table>`,
378
+ description: "Table with sticky first column"
379
+ }
380
+ ]
381
+ },
382
+ "dropdown-menu": {
383
+ name: "DropdownMenu",
384
+ description: "A dropdown menu component for displaying actions and options. Built on Radix UI with full keyboard navigation support.",
385
+ dependencies: [
386
+ "@radix-ui/react-dropdown-menu",
387
+ "clsx",
388
+ "tailwind-merge",
389
+ "lucide-react"
390
+ ],
391
+ props: [
392
+ {
393
+ name: "DropdownMenu",
394
+ type: "Root component",
395
+ required: true,
396
+ description: "Wrapper component that manages dropdown state"
397
+ },
398
+ {
399
+ name: "DropdownMenuTrigger",
400
+ type: "Trigger component",
401
+ required: true,
402
+ description: "The button that opens the dropdown"
403
+ },
404
+ {
405
+ name: "DropdownMenuContent",
406
+ type: "Content component",
407
+ required: true,
408
+ description: "Container for menu items"
409
+ },
410
+ {
411
+ name: "DropdownMenuItem",
412
+ type: "Item component",
413
+ required: false,
414
+ description: "Individual menu item"
415
+ },
416
+ {
417
+ name: "DropdownMenuCheckboxItem",
418
+ type: "Checkbox item component",
419
+ required: false,
420
+ description: "Menu item with checkbox"
421
+ },
422
+ {
423
+ name: "DropdownMenuRadioGroup",
424
+ type: "Radio group component",
425
+ required: false,
426
+ description: "Group of radio menu items"
427
+ },
428
+ {
429
+ name: "DropdownMenuSeparator",
430
+ type: "Separator component",
431
+ required: false,
432
+ description: "Visual separator between items"
433
+ }
434
+ ],
435
+ variants: [],
436
+ examples: [
437
+ {
438
+ title: "Basic Dropdown",
439
+ code: `<DropdownMenu>
440
+ <DropdownMenuTrigger asChild>
441
+ <Button variant="outline">Open Menu</Button>
442
+ </DropdownMenuTrigger>
443
+ <DropdownMenuContent>
444
+ <DropdownMenuItem>Profile</DropdownMenuItem>
445
+ <DropdownMenuItem>Settings</DropdownMenuItem>
446
+ <DropdownMenuSeparator />
447
+ <DropdownMenuItem>Logout</DropdownMenuItem>
448
+ </DropdownMenuContent>
449
+ </DropdownMenu>`,
450
+ description: "Simple dropdown with menu items"
451
+ },
452
+ {
453
+ title: "Dropdown with Checkbox Items",
454
+ code: `<DropdownMenu>
455
+ <DropdownMenuTrigger asChild>
456
+ <Button>Options</Button>
457
+ </DropdownMenuTrigger>
458
+ <DropdownMenuContent>
459
+ <DropdownMenuCheckboxItem checked={showStatus}>
460
+ Show Status Bar
461
+ </DropdownMenuCheckboxItem>
462
+ <DropdownMenuCheckboxItem checked={showPanel}>
463
+ Show Panel
464
+ </DropdownMenuCheckboxItem>
465
+ </DropdownMenuContent>
466
+ </DropdownMenu>`,
467
+ description: "Dropdown with checkable items"
468
+ },
469
+ {
470
+ title: "Dropdown with Submenu",
471
+ code: `<DropdownMenu>
472
+ <DropdownMenuTrigger asChild>
473
+ <Button>Actions</Button>
474
+ </DropdownMenuTrigger>
475
+ <DropdownMenuContent>
476
+ <DropdownMenuItem>New File</DropdownMenuItem>
477
+ <DropdownMenuSub>
478
+ <DropdownMenuSubTrigger>Share</DropdownMenuSubTrigger>
479
+ <DropdownMenuSubContent>
480
+ <DropdownMenuItem>Email</DropdownMenuItem>
481
+ <DropdownMenuItem>Slack</DropdownMenuItem>
482
+ </DropdownMenuSubContent>
483
+ </DropdownMenuSub>
484
+ </DropdownMenuContent>
485
+ </DropdownMenu>`,
486
+ description: "Nested dropdown with submenu"
487
+ }
488
+ ]
489
+ }
490
+ };
491
+ function getComponentNames() {
492
+ return Object.keys(componentMetadata);
493
+ }
494
+ function getComponent(name) {
495
+ return componentMetadata[name.toLowerCase()];
496
+ }
497
+
498
+ // src/server/tools/list-components.ts
499
+ function registerListComponents(server) {
500
+ server.tool(
501
+ "list-components",
502
+ "List all available myOperator UI components with their descriptions",
503
+ {},
504
+ async () => {
505
+ const components = getComponentNames().map((name) => ({
506
+ name: componentMetadata[name].name,
507
+ id: name,
508
+ description: componentMetadata[name].description,
509
+ dependencyCount: componentMetadata[name].dependencies.length
510
+ }));
511
+ return {
512
+ content: [
513
+ {
514
+ type: "text",
515
+ text: JSON.stringify(
516
+ {
517
+ totalComponents: components.length,
518
+ components,
519
+ usage: "Use get-component-metadata with the component id to get detailed information"
520
+ },
521
+ null,
522
+ 2
523
+ )
524
+ }
525
+ ]
526
+ };
527
+ }
528
+ );
529
+ }
530
+
531
+ // src/server/tools/get-component-metadata.ts
532
+ import { z } from "zod";
533
+ function registerGetComponentMetadata(server) {
534
+ server.tool(
535
+ "get-component-metadata",
536
+ "Get detailed metadata for a myOperator UI component including props, variants, and dependencies",
537
+ {
538
+ componentName: z.string().describe("The name of the component (e.g., 'button', 'badge', 'table')")
539
+ },
540
+ async ({ componentName }) => {
541
+ const component = getComponent(componentName);
542
+ if (!component) {
543
+ const availableComponents = getComponentNames().join(", ");
544
+ return {
545
+ content: [
546
+ {
547
+ type: "text",
548
+ text: `Error: Component "${componentName}" not found. Available components: ${availableComponents}`
549
+ }
550
+ ],
551
+ isError: true
552
+ };
553
+ }
554
+ const metadata = {
555
+ name: component.name,
556
+ description: component.description,
557
+ dependencies: component.dependencies,
558
+ props: component.props,
559
+ variants: component.variants,
560
+ import: `import { ${component.name} } from "@/components/ui/${componentName}"`
561
+ };
562
+ return {
563
+ content: [
564
+ {
565
+ type: "text",
566
+ text: JSON.stringify(metadata, null, 2)
567
+ }
568
+ ]
569
+ };
570
+ }
571
+ );
572
+ }
573
+
574
+ // src/server/tools/get-component-examples.ts
575
+ import { z as z2 } from "zod";
576
+ function registerGetComponentExamples(server) {
577
+ server.tool(
578
+ "get-component-examples",
579
+ "Get React code examples for a myOperator UI component",
580
+ {
581
+ componentName: z2.string().describe("The name of the component (e.g., 'button', 'badge', 'table')"),
582
+ exampleType: z2.string().optional().describe("Filter examples by type/title (optional)")
583
+ },
584
+ async ({ componentName, exampleType }) => {
585
+ const component = getComponent(componentName);
586
+ if (!component) {
587
+ const availableComponents = getComponentNames().join(", ");
588
+ return {
589
+ content: [
590
+ {
591
+ type: "text",
592
+ text: `Error: Component "${componentName}" not found. Available components: ${availableComponents}`
593
+ }
594
+ ],
595
+ isError: true
596
+ };
597
+ }
598
+ let examples = component.examples;
599
+ if (exampleType) {
600
+ examples = examples.filter(
601
+ (example) => example.title.toLowerCase().includes(exampleType.toLowerCase())
602
+ );
603
+ }
604
+ if (examples.length === 0) {
605
+ return {
606
+ content: [
607
+ {
608
+ type: "text",
609
+ text: `No examples found${exampleType ? ` matching "${exampleType}"` : ""} for component "${componentName}"`
610
+ }
611
+ ]
612
+ };
613
+ }
614
+ const result = {
615
+ component: component.name,
616
+ import: `import { ${component.name} } from "@/components/ui/${componentName}"`,
617
+ examplesCount: examples.length,
618
+ examples: examples.map((example) => ({
619
+ title: example.title,
620
+ description: example.description,
621
+ code: example.code
622
+ }))
623
+ };
624
+ return {
625
+ content: [
626
+ {
627
+ type: "text",
628
+ text: JSON.stringify(result, null, 2)
629
+ }
630
+ ]
631
+ };
632
+ }
633
+ );
634
+ }
635
+
636
+ // src/server/tools/list-design-tokens.ts
637
+ import { z as z3 } from "zod";
638
+
639
+ // src/data/tokens.ts
640
+ var designTokens = [
641
+ // Primary Colors
642
+ {
643
+ name: "Primary",
644
+ value: "#343E55",
645
+ category: "colors",
646
+ description: "Primary brand color used for buttons, links, and emphasis"
647
+ },
648
+ {
649
+ name: "Primary Hover",
650
+ value: "#343E55/90",
651
+ category: "colors",
652
+ description: "Primary color at 90% opacity for hover states"
653
+ },
654
+ // Status Colors
655
+ {
656
+ name: "Active/Success",
657
+ value: "#00A651",
658
+ category: "colors",
659
+ description: "Green color for active/success states"
660
+ },
661
+ {
662
+ name: "Active Background",
663
+ value: "#E5FFF5",
664
+ category: "colors",
665
+ description: "Light green background for active badges"
666
+ },
667
+ {
668
+ name: "Failed/Error",
669
+ value: "#FF3B3B",
670
+ category: "colors",
671
+ description: "Red color for failed/error states"
672
+ },
673
+ {
674
+ name: "Failed Background",
675
+ value: "#FFECEC",
676
+ category: "colors",
677
+ description: "Light red background for failed badges"
678
+ },
679
+ {
680
+ name: "Warning",
681
+ value: "#F59E0B",
682
+ category: "colors",
683
+ description: "Amber color for warning states"
684
+ },
685
+ {
686
+ name: "Warning Background",
687
+ value: "#FFF8E5",
688
+ category: "colors",
689
+ description: "Light amber background for warnings"
690
+ },
691
+ // Neutral Colors
692
+ {
693
+ name: "Text Primary",
694
+ value: "#333333",
695
+ category: "colors",
696
+ description: "Primary text color"
697
+ },
698
+ {
699
+ name: "Text Secondary",
700
+ value: "#6B7280",
701
+ category: "colors",
702
+ description: "Secondary/muted text color"
703
+ },
704
+ {
705
+ name: "Text Tertiary",
706
+ value: "#9CA3AF",
707
+ category: "colors",
708
+ description: "Tertiary text color for hints"
709
+ },
710
+ {
711
+ name: "Border",
712
+ value: "#E5E7EB",
713
+ category: "colors",
714
+ description: "Default border color"
715
+ },
716
+ {
717
+ name: "Background Muted",
718
+ value: "#F3F4F6",
719
+ category: "colors",
720
+ description: "Muted background for tags, disabled states"
721
+ },
722
+ {
723
+ name: "Background Hover",
724
+ value: "#F9FAFB",
725
+ category: "colors",
726
+ description: "Hover background for table rows, menu items"
727
+ },
728
+ {
729
+ name: "Background Highlight",
730
+ value: "#EBF5FF",
731
+ category: "colors",
732
+ description: "Blue highlight background for selected rows"
733
+ },
734
+ // CSS Variables
735
+ {
736
+ name: "background",
737
+ value: "0 0% 100%",
738
+ category: "colors",
739
+ cssVariable: "--background",
740
+ description: "HSL value for page background"
741
+ },
742
+ {
743
+ name: "foreground",
744
+ value: "222.2 84% 4.9%",
745
+ category: "colors",
746
+ cssVariable: "--foreground",
747
+ description: "HSL value for default text"
748
+ },
749
+ {
750
+ name: "primary",
751
+ value: "222.2 47.4% 11.2%",
752
+ category: "colors",
753
+ cssVariable: "--primary",
754
+ description: "HSL value for primary color"
755
+ },
756
+ {
757
+ name: "secondary",
758
+ value: "210 40% 96.1%",
759
+ category: "colors",
760
+ cssVariable: "--secondary",
761
+ description: "HSL value for secondary color"
762
+ },
763
+ {
764
+ name: "destructive",
765
+ value: "0 84.2% 60.2%",
766
+ category: "colors",
767
+ cssVariable: "--destructive",
768
+ description: "HSL value for destructive actions"
769
+ },
770
+ {
771
+ name: "muted",
772
+ value: "210 40% 96.1%",
773
+ category: "colors",
774
+ cssVariable: "--muted",
775
+ description: "HSL value for muted elements"
776
+ },
777
+ {
778
+ name: "accent",
779
+ value: "210 40% 96.1%",
780
+ category: "colors",
781
+ cssVariable: "--accent",
782
+ description: "HSL value for accents"
783
+ },
784
+ {
785
+ name: "border",
786
+ value: "214.3 31.8% 91.4%",
787
+ category: "colors",
788
+ cssVariable: "--border",
789
+ description: "HSL value for borders"
790
+ },
791
+ {
792
+ name: "ring",
793
+ value: "222.2 84% 4.9%",
794
+ category: "colors",
795
+ cssVariable: "--ring",
796
+ description: "HSL value for focus rings"
797
+ },
798
+ // Spacing
799
+ {
800
+ name: "Spacing XS",
801
+ value: "0.5rem (8px)",
802
+ category: "spacing",
803
+ description: "Extra small spacing"
804
+ },
805
+ {
806
+ name: "Spacing SM",
807
+ value: "0.75rem (12px)",
808
+ category: "spacing",
809
+ description: "Small spacing"
810
+ },
811
+ {
812
+ name: "Spacing MD",
813
+ value: "1rem (16px)",
814
+ category: "spacing",
815
+ description: "Medium spacing (default)"
816
+ },
817
+ {
818
+ name: "Spacing LG",
819
+ value: "1.5rem (24px)",
820
+ category: "spacing",
821
+ description: "Large spacing"
822
+ },
823
+ {
824
+ name: "Spacing XL",
825
+ value: "2rem (32px)",
826
+ category: "spacing",
827
+ description: "Extra large spacing"
828
+ },
829
+ // Button Padding
830
+ {
831
+ name: "Button SM Padding",
832
+ value: "py-2 px-3 (8px 12px)",
833
+ category: "spacing",
834
+ description: "Small button padding"
835
+ },
836
+ {
837
+ name: "Button Default Padding",
838
+ value: "py-2.5 px-4 (10px 16px)",
839
+ category: "spacing",
840
+ description: "Default button padding"
841
+ },
842
+ {
843
+ name: "Button LG Padding",
844
+ value: "py-3 px-6 (12px 24px)",
845
+ category: "spacing",
846
+ description: "Large button padding"
847
+ },
848
+ // Border Radius
849
+ {
850
+ name: "Radius",
851
+ value: "0.5rem (8px)",
852
+ category: "radius",
853
+ cssVariable: "--radius",
854
+ description: "Default border radius"
855
+ },
856
+ {
857
+ name: "Radius SM",
858
+ value: "0.25rem (4px)",
859
+ category: "radius",
860
+ description: "Small border radius (buttons)"
861
+ },
862
+ {
863
+ name: "Radius MD",
864
+ value: "0.375rem (6px)",
865
+ category: "radius",
866
+ description: "Medium border radius"
867
+ },
868
+ {
869
+ name: "Radius LG",
870
+ value: "0.5rem (8px)",
871
+ category: "radius",
872
+ description: "Large border radius (cards, tables)"
873
+ },
874
+ {
875
+ name: "Radius Full",
876
+ value: "9999px",
877
+ category: "radius",
878
+ description: "Full/pill border radius (badges)"
879
+ },
880
+ // Typography
881
+ {
882
+ name: "Text XS",
883
+ value: "0.75rem (12px)",
884
+ category: "typography",
885
+ description: "Extra small text size"
886
+ },
887
+ {
888
+ name: "Text SM",
889
+ value: "0.875rem (14px)",
890
+ category: "typography",
891
+ description: "Small text size (default for components)"
892
+ },
893
+ {
894
+ name: "Text Base",
895
+ value: "1rem (16px)",
896
+ category: "typography",
897
+ description: "Base text size"
898
+ },
899
+ {
900
+ name: "Font Normal",
901
+ value: "400",
902
+ category: "typography",
903
+ description: "Normal font weight"
904
+ },
905
+ {
906
+ name: "Font Medium",
907
+ value: "500",
908
+ category: "typography",
909
+ description: "Medium font weight (buttons)"
910
+ },
911
+ {
912
+ name: "Font Semibold",
913
+ value: "600",
914
+ category: "typography",
915
+ description: "Semibold font weight (badges, labels)"
916
+ }
917
+ ];
918
+ function getTokensByCategory(category) {
919
+ if (!category) {
920
+ return designTokens;
921
+ }
922
+ return designTokens.filter((token) => token.category === category);
923
+ }
924
+ function getCategories() {
925
+ return [...new Set(designTokens.map((token) => token.category))];
926
+ }
927
+
928
+ // src/server/tools/list-design-tokens.ts
929
+ function registerListDesignTokens(server) {
930
+ server.tool(
931
+ "list-design-tokens",
932
+ "List design tokens (colors, spacing, radius, typography) from the myOperator UI design system",
933
+ {
934
+ category: z3.enum(["colors", "spacing", "radius", "typography"]).optional().describe("Filter tokens by category (optional)")
935
+ },
936
+ async ({ category }) => {
937
+ const tokens = getTokensByCategory(category);
938
+ const categories = getCategories();
939
+ const result = {
940
+ totalTokens: tokens.length,
941
+ availableCategories: categories,
942
+ selectedCategory: category || "all",
943
+ tokens: tokens.map((token) => ({
944
+ name: token.name,
945
+ value: token.value,
946
+ category: token.category,
947
+ cssVariable: token.cssVariable,
948
+ description: token.description
949
+ })),
950
+ usage: {
951
+ cssVariables: "Use CSS variables like: color: hsl(var(--primary));",
952
+ hardcodedColors: "For Bootstrap compatibility, some colors are hardcoded (e.g., bg-[#343E55])",
953
+ tailwindClasses: "Components use Tailwind classes with these token values"
954
+ }
955
+ };
956
+ return {
957
+ content: [
958
+ {
959
+ type: "text",
960
+ text: JSON.stringify(result, null, 2)
961
+ }
962
+ ]
963
+ };
964
+ }
965
+ );
966
+ }
967
+
968
+ // src/server/tools/get-component-accessibility.ts
969
+ import { z as z4 } from "zod";
970
+
971
+ // src/data/accessibility.ts
972
+ var accessibilityGuidelines = {
973
+ button: {
974
+ component: "Button",
975
+ guidelines: [
976
+ {
977
+ category: "Semantic HTML",
978
+ items: [
979
+ "Uses native <button> element for proper semantics",
980
+ "Supports asChild prop to render as other elements while maintaining accessibility",
981
+ "Automatically handles disabled state"
982
+ ]
983
+ },
984
+ {
985
+ category: "Visual Feedback",
986
+ items: [
987
+ "Has visible focus ring using focus-visible for keyboard users",
988
+ "Disabled state has reduced opacity (50%)",
989
+ "Loading state shows spinner and maintains text for context"
990
+ ]
991
+ },
992
+ {
993
+ category: "Best Practices",
994
+ items: [
995
+ "Use aria-label for icon-only buttons",
996
+ "Loading state automatically disables the button to prevent double submission",
997
+ "Destructive variant uses red color to indicate dangerous actions"
998
+ ]
999
+ }
1000
+ ],
1001
+ ariaAttributes: [
1002
+ {
1003
+ attribute: "aria-label",
1004
+ usage: "Required for icon-only buttons to provide accessible name"
1005
+ },
1006
+ {
1007
+ attribute: "aria-disabled",
1008
+ usage: "Set automatically when disabled or loading"
1009
+ },
1010
+ {
1011
+ attribute: "aria-busy",
1012
+ usage: "Consider adding when loading prop is true"
1013
+ }
1014
+ ],
1015
+ keyboardSupport: [
1016
+ { key: "Enter", action: "Activates the button" },
1017
+ { key: "Space", action: "Activates the button" },
1018
+ { key: "Tab", action: "Moves focus to/from the button" }
1019
+ ]
1020
+ },
1021
+ badge: {
1022
+ component: "Badge",
1023
+ guidelines: [
1024
+ {
1025
+ category: "Semantic Usage",
1026
+ items: [
1027
+ "Rendered as <div> - consider adding role='status' for dynamic status badges",
1028
+ "Color alone should not be the only indicator of status",
1029
+ "Include descriptive text alongside color"
1030
+ ]
1031
+ },
1032
+ {
1033
+ category: "Color Contrast",
1034
+ items: [
1035
+ "Active variant: Green text on light green background (passes WCAG AA)",
1036
+ "Failed variant: Red text on light red background (passes WCAG AA)",
1037
+ "Disabled variant: Gray text on gray background (passes WCAG AA)"
1038
+ ]
1039
+ },
1040
+ {
1041
+ category: "Best Practices",
1042
+ items: [
1043
+ "Icons in badges should be decorative and not the only indicator",
1044
+ "Screen readers will read the badge text content",
1045
+ "Consider aria-live='polite' for status badges that update dynamically"
1046
+ ]
1047
+ }
1048
+ ],
1049
+ ariaAttributes: [
1050
+ {
1051
+ attribute: "role='status'",
1052
+ usage: "Add when badge represents live status that updates"
1053
+ },
1054
+ {
1055
+ attribute: "aria-live='polite'",
1056
+ usage: "Add for dynamically updating status badges"
1057
+ }
1058
+ ],
1059
+ keyboardSupport: [
1060
+ { key: "N/A", action: "Badges are not interactive by default" }
1061
+ ]
1062
+ },
1063
+ tag: {
1064
+ component: "Tag",
1065
+ guidelines: [
1066
+ {
1067
+ category: "Interactive Tags",
1068
+ items: [
1069
+ "When interactive=true, component adds role='button' and tabIndex=0",
1070
+ "Selected state uses aria-selected attribute",
1071
+ "Interactive tags respond to keyboard activation"
1072
+ ]
1073
+ },
1074
+ {
1075
+ category: "Visual Design",
1076
+ items: [
1077
+ "Selected state has visible ring outline for clear visual feedback",
1078
+ "Interactive tags have hover and active state styling",
1079
+ "Focus state is visible for keyboard navigation"
1080
+ ]
1081
+ },
1082
+ {
1083
+ category: "Best Practices",
1084
+ items: [
1085
+ "Use label prop for bold category prefixes to improve scannability",
1086
+ "Group related tags together for easier comprehension",
1087
+ "Consider using aria-describedby for additional context if needed"
1088
+ ]
1089
+ }
1090
+ ],
1091
+ ariaAttributes: [
1092
+ {
1093
+ attribute: "role='button'",
1094
+ usage: "Automatically added when interactive=true"
1095
+ },
1096
+ {
1097
+ attribute: "tabIndex='0'",
1098
+ usage: "Automatically added when interactive=true"
1099
+ },
1100
+ {
1101
+ attribute: "aria-selected",
1102
+ usage: "Reflects the selected prop state"
1103
+ }
1104
+ ],
1105
+ keyboardSupport: [
1106
+ { key: "Enter", action: "Activates interactive tag" },
1107
+ { key: "Space", action: "Activates interactive tag" },
1108
+ { key: "Tab", action: "Moves focus between interactive tags" }
1109
+ ]
1110
+ },
1111
+ table: {
1112
+ component: "Table",
1113
+ guidelines: [
1114
+ {
1115
+ category: "Semantic Structure",
1116
+ items: [
1117
+ "Uses native <table>, <thead>, <tbody>, <tr>, <th>, <td> elements",
1118
+ "TableHead uses <th> with proper text-left alignment",
1119
+ "Supports sticky columns that maintain context during horizontal scroll"
1120
+ ]
1121
+ },
1122
+ {
1123
+ category: "Visual Clarity",
1124
+ items: [
1125
+ "Highlighted rows use distinct background color",
1126
+ "Hover states on rows help track which row is being examined",
1127
+ "Sort direction indicators are visible in table headers",
1128
+ "Info tooltips can be added to headers for additional context"
1129
+ ]
1130
+ },
1131
+ {
1132
+ category: "Loading & Empty States",
1133
+ items: [
1134
+ "TableSkeleton provides visual loading feedback",
1135
+ "TableEmpty provides clear message when no data is available",
1136
+ "Skeleton animation indicates data is loading"
1137
+ ]
1138
+ }
1139
+ ],
1140
+ ariaAttributes: [
1141
+ {
1142
+ attribute: "scope='col'",
1143
+ usage: "Consider adding to TableHead for better header association"
1144
+ },
1145
+ {
1146
+ attribute: "aria-sort",
1147
+ usage: "Add to sortable columns to indicate sort state"
1148
+ },
1149
+ {
1150
+ attribute: "aria-describedby",
1151
+ usage: "Link to TableCaption for table description"
1152
+ }
1153
+ ],
1154
+ keyboardSupport: [
1155
+ { key: "Tab", action: "Navigate between interactive elements in cells" },
1156
+ { key: "Arrow keys", action: "Consider adding for cell navigation" }
1157
+ ]
1158
+ },
1159
+ "dropdown-menu": {
1160
+ component: "DropdownMenu",
1161
+ guidelines: [
1162
+ {
1163
+ category: "Keyboard Navigation",
1164
+ items: [
1165
+ "Full arrow key navigation between menu items",
1166
+ "Escape closes the menu and returns focus to trigger",
1167
+ "Enter/Space activates the focused menu item",
1168
+ "Type-ahead search to jump to items starting with typed character"
1169
+ ]
1170
+ },
1171
+ {
1172
+ category: "Focus Management",
1173
+ items: [
1174
+ "Focus is trapped within menu when open",
1175
+ "Focus returns to trigger when menu closes",
1176
+ "Submenus are accessible via arrow keys"
1177
+ ]
1178
+ },
1179
+ {
1180
+ category: "Screen Reader Support",
1181
+ items: [
1182
+ "Proper ARIA roles assigned automatically by Radix",
1183
+ "Menu items announce their role and state",
1184
+ "Checkbox and radio items announce their checked state"
1185
+ ]
1186
+ }
1187
+ ],
1188
+ ariaAttributes: [
1189
+ {
1190
+ attribute: "role='menu'",
1191
+ usage: "Automatically applied to DropdownMenuContent"
1192
+ },
1193
+ {
1194
+ attribute: "role='menuitem'",
1195
+ usage: "Automatically applied to DropdownMenuItem"
1196
+ },
1197
+ {
1198
+ attribute: "role='menuitemcheckbox'",
1199
+ usage: "Automatically applied to DropdownMenuCheckboxItem"
1200
+ },
1201
+ {
1202
+ attribute: "role='menuitemradio'",
1203
+ usage: "Automatically applied to DropdownMenuRadioItem"
1204
+ },
1205
+ {
1206
+ attribute: "aria-expanded",
1207
+ usage: "Set on trigger based on menu open state"
1208
+ },
1209
+ {
1210
+ attribute: "aria-haspopup",
1211
+ usage: "Set on trigger to indicate menu presence"
1212
+ }
1213
+ ],
1214
+ keyboardSupport: [
1215
+ { key: "Enter / Space", action: "Opens menu from trigger, activates item" },
1216
+ { key: "ArrowDown", action: "Opens menu, moves to next item" },
1217
+ { key: "ArrowUp", action: "Moves to previous item" },
1218
+ { key: "ArrowRight", action: "Opens submenu" },
1219
+ { key: "ArrowLeft", action: "Closes submenu" },
1220
+ { key: "Escape", action: "Closes menu" },
1221
+ { key: "Tab", action: "Closes menu and moves focus" },
1222
+ { key: "Home", action: "Moves to first item" },
1223
+ { key: "End", action: "Moves to last item" }
1224
+ ]
1225
+ }
1226
+ };
1227
+ function getAccessibilityGuideline(componentName) {
1228
+ return accessibilityGuidelines[componentName.toLowerCase()];
1229
+ }
1230
+ function getComponentsWithGuidelines() {
1231
+ return Object.keys(accessibilityGuidelines);
1232
+ }
1233
+
1234
+ // src/server/tools/get-component-accessibility.ts
1235
+ function registerGetComponentAccessibility(server) {
1236
+ server.tool(
1237
+ "get-component-accessibility",
1238
+ "Get accessibility guidelines and requirements for a myOperator UI component",
1239
+ {
1240
+ componentName: z4.string().describe("The name of the component (e.g., 'button', 'badge', 'table')")
1241
+ },
1242
+ async ({ componentName }) => {
1243
+ const guideline = getAccessibilityGuideline(componentName);
1244
+ if (!guideline) {
1245
+ const availableComponents = getComponentsWithGuidelines().join(", ");
1246
+ return {
1247
+ content: [
1248
+ {
1249
+ type: "text",
1250
+ text: `Error: Accessibility guidelines for "${componentName}" not found. Available components: ${availableComponents}`
1251
+ }
1252
+ ],
1253
+ isError: true
1254
+ };
1255
+ }
1256
+ const result = {
1257
+ component: guideline.component,
1258
+ guidelines: guideline.guidelines,
1259
+ ariaAttributes: guideline.ariaAttributes,
1260
+ keyboardSupport: guideline.keyboardSupport,
1261
+ wcagCompliance: {
1262
+ colorContrast: "All components meet WCAG 2.1 AA color contrast requirements",
1263
+ focusVisible: "Keyboard focus is visible on all interactive elements",
1264
+ semanticHTML: "Native HTML elements used where appropriate"
1265
+ }
1266
+ };
1267
+ return {
1268
+ content: [
1269
+ {
1270
+ type: "text",
1271
+ text: JSON.stringify(result, null, 2)
1272
+ }
1273
+ ]
1274
+ };
1275
+ }
1276
+ );
1277
+ }
1278
+
1279
+ // src/server/index.ts
1280
+ function createServer() {
1281
+ const server = new McpServer({
1282
+ name: "myoperator-mcp",
1283
+ version: "0.0.1"
1284
+ });
1285
+ registerListComponents(server);
1286
+ registerGetComponentMetadata(server);
1287
+ registerGetComponentExamples(server);
1288
+ registerListDesignTokens(server);
1289
+ registerGetComponentAccessibility(server);
1290
+ return server;
1291
+ }
1292
+
1293
+ // src/index.ts
1294
+ async function main() {
1295
+ const server = createServer();
1296
+ const transport = new StdioServerTransport();
1297
+ await server.connect(transport);
1298
+ console.error("myOperator MCP server running");
1299
+ }
1300
+ main().catch((error) => {
1301
+ console.error("Failed to start MCP server:", error);
1302
+ process.exit(1);
1303
+ });