@framingui/mcp-server 0.6.26 → 0.6.28

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 (146) hide show
  1. package/README.md +4 -3
  2. package/dist/__tests__/react-native-contract.test.d.ts +2 -0
  3. package/dist/__tests__/react-native-contract.test.d.ts.map +1 -0
  4. package/dist/__tests__/react-native-contract.test.js +50 -0
  5. package/dist/__tests__/react-native-contract.test.js.map +1 -0
  6. package/dist/auth/state.d.ts +16 -0
  7. package/dist/auth/state.d.ts.map +1 -1
  8. package/dist/auth/state.js +54 -15
  9. package/dist/auth/state.js.map +1 -1
  10. package/dist/auth/verify.d.ts +9 -0
  11. package/dist/auth/verify.d.ts.map +1 -1
  12. package/dist/auth/verify.js.map +1 -1
  13. package/dist/billing/quota-policy.d.ts +13 -0
  14. package/dist/billing/quota-policy.d.ts.map +1 -0
  15. package/dist/billing/quota-policy.js +32 -0
  16. package/dist/billing/quota-policy.js.map +1 -0
  17. package/dist/billing/tool-metering.d.ts +10 -0
  18. package/dist/billing/tool-metering.d.ts.map +1 -0
  19. package/dist/billing/tool-metering.js +126 -0
  20. package/dist/billing/tool-metering.js.map +1 -0
  21. package/dist/billing/usage-ledger.d.ts +61 -0
  22. package/dist/billing/usage-ledger.d.ts.map +1 -0
  23. package/dist/billing/usage-ledger.js +102 -0
  24. package/dist/billing/usage-ledger.js.map +1 -0
  25. package/dist/billing/usage-sync.d.ts +3 -0
  26. package/dist/billing/usage-sync.d.ts.map +1 -0
  27. package/dist/billing/usage-sync.js +40 -0
  28. package/dist/billing/usage-sync.js.map +1 -0
  29. package/dist/cli/agent-md-templates.d.ts.map +1 -1
  30. package/dist/cli/agent-md-templates.js +58 -38
  31. package/dist/cli/agent-md-templates.js.map +1 -1
  32. package/dist/cli/commands.d.ts +2 -0
  33. package/dist/cli/commands.d.ts.map +1 -0
  34. package/dist/cli/commands.js +20 -0
  35. package/dist/cli/commands.js.map +1 -0
  36. package/dist/cli/guide-template.d.ts.map +1 -1
  37. package/dist/cli/guide-template.js +20 -36
  38. package/dist/cli/guide-template.js.map +1 -1
  39. package/dist/cli/help.d.ts +5 -0
  40. package/dist/cli/help.d.ts.map +1 -0
  41. package/dist/cli/help.js +59 -0
  42. package/dist/cli/help.js.map +1 -0
  43. package/dist/cli/init.d.ts +2 -3
  44. package/dist/cli/init.d.ts.map +1 -1
  45. package/dist/cli/init.js +33 -48
  46. package/dist/cli/init.js.map +1 -1
  47. package/dist/cli/package-manager.d.ts +5 -0
  48. package/dist/cli/package-manager.d.ts.map +1 -0
  49. package/dist/cli/package-manager.js +34 -0
  50. package/dist/cli/package-manager.js.map +1 -0
  51. package/dist/cli/update.d.ts +8 -0
  52. package/dist/cli/update.d.ts.map +1 -0
  53. package/dist/cli/update.js +53 -0
  54. package/dist/cli/update.js.map +1 -0
  55. package/dist/commands/slash-command-adapters.d.ts +9 -0
  56. package/dist/commands/slash-command-adapters.d.ts.map +1 -0
  57. package/dist/commands/slash-command-adapters.js +116 -0
  58. package/dist/commands/slash-command-adapters.js.map +1 -0
  59. package/dist/data/component-fallback-catalog.d.ts +26 -0
  60. package/dist/data/component-fallback-catalog.d.ts.map +1 -0
  61. package/dist/data/component-fallback-catalog.js +149 -0
  62. package/dist/data/component-fallback-catalog.js.map +1 -0
  63. package/dist/data/component-props.d.ts +30 -0
  64. package/dist/data/component-props.d.ts.map +1 -0
  65. package/dist/data/component-props.js +537 -0
  66. package/dist/data/component-props.js.map +1 -0
  67. package/dist/data/component-registry.d.ts +30 -0
  68. package/dist/data/component-registry.d.ts.map +1 -0
  69. package/dist/data/component-registry.js +320 -0
  70. package/dist/data/component-registry.js.map +1 -0
  71. package/dist/data/examples/screen-examples.d.ts +38 -0
  72. package/dist/data/examples/screen-examples.d.ts.map +1 -0
  73. package/dist/data/examples/screen-examples.js +500 -0
  74. package/dist/data/examples/screen-examples.js.map +1 -0
  75. package/dist/data/react-native-runtime-catalog.d.ts +24 -0
  76. package/dist/data/react-native-runtime-catalog.d.ts.map +1 -0
  77. package/dist/data/react-native-runtime-catalog.js +265 -0
  78. package/dist/data/react-native-runtime-catalog.js.map +1 -0
  79. package/dist/index.js +225 -205
  80. package/dist/index.js.map +1 -1
  81. package/dist/platform-support.d.ts +22 -0
  82. package/dist/platform-support.d.ts.map +1 -0
  83. package/dist/platform-support.js +148 -0
  84. package/dist/platform-support.js.map +1 -0
  85. package/dist/project-context-resolution.d.ts +7 -0
  86. package/dist/project-context-resolution.d.ts.map +1 -0
  87. package/dist/project-context-resolution.js +21 -0
  88. package/dist/project-context-resolution.js.map +1 -0
  89. package/dist/project-context-state.d.ts +12 -0
  90. package/dist/project-context-state.d.ts.map +1 -0
  91. package/dist/project-context-state.js +14 -0
  92. package/dist/project-context-state.js.map +1 -0
  93. package/dist/project-context.d.ts +15 -0
  94. package/dist/project-context.d.ts.map +1 -0
  95. package/dist/project-context.js +78 -0
  96. package/dist/project-context.js.map +1 -0
  97. package/dist/prompts/doctor-workflow.js +1 -1
  98. package/dist/prompts/getting-started.d.ts.map +1 -1
  99. package/dist/prompts/getting-started.js +36 -15
  100. package/dist/prompts/getting-started.js.map +1 -1
  101. package/dist/prompts/screen-workflow.d.ts.map +1 -1
  102. package/dist/prompts/screen-workflow.js +54 -3
  103. package/dist/prompts/screen-workflow.js.map +1 -1
  104. package/dist/schemas/mcp-schemas.d.ts +954 -309
  105. package/dist/schemas/mcp-schemas.d.ts.map +1 -1
  106. package/dist/schemas/mcp-schemas.js +130 -1
  107. package/dist/schemas/mcp-schemas.js.map +1 -1
  108. package/dist/tools/detect-project-context.d.ts +5 -0
  109. package/dist/tools/detect-project-context.d.ts.map +1 -0
  110. package/dist/tools/detect-project-context.js +36 -0
  111. package/dist/tools/detect-project-context.js.map +1 -0
  112. package/dist/tools/get-screen-generation-context.d.ts +3 -1
  113. package/dist/tools/get-screen-generation-context.d.ts.map +1 -1
  114. package/dist/tools/get-screen-generation-context.js +120 -20
  115. package/dist/tools/get-screen-generation-context.js.map +1 -1
  116. package/dist/tools/list-components.d.ts.map +1 -1
  117. package/dist/tools/list-components.js +42 -3
  118. package/dist/tools/list-components.js.map +1 -1
  119. package/dist/tools/list-screen-templates.d.ts +3 -1
  120. package/dist/tools/list-screen-templates.d.ts.map +1 -1
  121. package/dist/tools/list-screen-templates.js +3 -3
  122. package/dist/tools/list-screen-templates.js.map +1 -1
  123. package/dist/tools/preview-component.d.ts.map +1 -1
  124. package/dist/tools/preview-component.js +57 -7
  125. package/dist/tools/preview-component.js.map +1 -1
  126. package/dist/tools/preview-theme.d.ts +3 -1
  127. package/dist/tools/preview-theme.d.ts.map +1 -1
  128. package/dist/tools/preview-theme.js +48 -21
  129. package/dist/tools/preview-theme.js.map +1 -1
  130. package/dist/tools/theme-authority.js +1 -1
  131. package/dist/tools/theme-authority.js.map +1 -1
  132. package/dist/tools/validate-environment.d.ts +3 -1
  133. package/dist/tools/validate-environment.d.ts.map +1 -1
  134. package/dist/tools/validate-environment.js +102 -9
  135. package/dist/tools/validate-environment.js.map +1 -1
  136. package/dist/tools/validate-screen-definition.d.ts.map +1 -1
  137. package/dist/tools/validate-screen-definition.js +41 -7
  138. package/dist/tools/validate-screen-definition.js.map +1 -1
  139. package/dist/tools/whoami.d.ts.map +1 -1
  140. package/dist/tools/whoami.js +27 -17
  141. package/dist/tools/whoami.js.map +1 -1
  142. package/dist/utils/style-contract-reader.d.ts +10 -0
  143. package/dist/utils/style-contract-reader.d.ts.map +1 -0
  144. package/dist/utils/style-contract-reader.js +67 -0
  145. package/dist/utils/style-contract-reader.js.map +1 -0
  146. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -24,10 +24,65 @@ import { previewScreenTemplateTool } from './tools/preview-screen-template.js';
24
24
  import { getScreenGenerationContextTool } from './tools/get-screen-generation-context.js';
25
25
  import { validateScreenDefinitionTool } from './tools/validate-screen-definition.js';
26
26
  import { validateEnvironmentTool } from './tools/validate-environment.js';
27
+ import { detectProjectContextTool } from './tools/detect-project-context.js';
27
28
  import { whoamiTool } from './tools/whoami.js';
28
29
  import { readPackageJson } from './utils/package-json-reader.js';
29
30
  import { getPromptDefinition, listPromptDefinitions } from './prompts/prompt-catalog.js';
30
- import { WhoamiInputSchema, GenerateBlueprintInputSchema, PreviewThemeInputSchema, ListThemesInputSchema, ExportScreenInputSchema, ValidateScreenInputSchema, ListTokensInputSchema, ListIconLibrariesInputSchema, PreviewIconLibraryInputSchema, ListComponentsInputSchema, PreviewComponentInputSchema, ListScreenTemplatesInputSchema, PreviewScreenTemplateInputSchema, GetScreenGenerationContextInputSchema, ValidateScreenDefinitionInputSchema, GenerateScreenInputSchema, ValidateEnvironmentInputSchema, } from './schemas/mcp-schemas.js';
31
+ import { getAuthData } from './auth/state.js';
32
+ import { recordToolUsage, getUsageQuotaSnapshot, } from './billing/usage-ledger.js';
33
+ import { evaluateQuotaPolicy } from './billing/quota-policy.js';
34
+ import { syncUsageEvent } from './billing/usage-sync.js';
35
+ import { WhoamiInputSchema, GenerateBlueprintInputSchema, PreviewThemeInputSchema, ListThemesInputSchema, ExportScreenInputSchema, ValidateScreenInputSchema, ListTokensInputSchema, ListIconLibrariesInputSchema, PreviewIconLibraryInputSchema, ListComponentsInputSchema, PreviewComponentInputSchema, ListScreenTemplatesInputSchema, PreviewScreenTemplateInputSchema, GetScreenGenerationContextInputSchema, ValidateScreenDefinitionInputSchema, GenerateScreenInputSchema, ValidateEnvironmentInputSchema, DetectProjectContextInputSchema, } from './schemas/mcp-schemas.js';
36
+ function buildToolResponse(result) {
37
+ return {
38
+ content: [
39
+ {
40
+ type: 'text',
41
+ text: JSON.stringify(result, null, 2),
42
+ },
43
+ ],
44
+ };
45
+ }
46
+ function inferUsageOutcome(result) {
47
+ if (typeof result === 'object' &&
48
+ result !== null &&
49
+ 'success' in result &&
50
+ result.success === false) {
51
+ return 'tool_error';
52
+ }
53
+ return 'success';
54
+ }
55
+ function meterToolCall(name, outcome) {
56
+ const authData = getAuthData();
57
+ const entry = recordToolUsage({
58
+ toolName: name,
59
+ outcome,
60
+ userId: authData?.user?.id ?? null,
61
+ plan: authData?.user?.plan ?? null,
62
+ });
63
+ void syncUsageEvent(entry);
64
+ }
65
+ function getQuotaPolicyDecision(name) {
66
+ const authData = getAuthData();
67
+ const snapshot = getUsageQuotaSnapshot({
68
+ userId: authData?.user?.id ?? null,
69
+ plan: authData?.user?.plan ?? null,
70
+ paidQuotaEntitlement: authData?.quotaEntitlement ?? null,
71
+ });
72
+ return evaluateQuotaPolicy({
73
+ toolName: name,
74
+ snapshot,
75
+ });
76
+ }
77
+ function attachQuotaWarning(result, warning) {
78
+ if (!warning || typeof result !== 'object' || result === null || Array.isArray(result)) {
79
+ return result;
80
+ }
81
+ return {
82
+ ...result,
83
+ quotaWarning: warning,
84
+ };
85
+ }
31
86
  const packageJsonResult = readPackageJson(fileURLToPath(new URL('../package.json', import.meta.url)));
32
87
  const serverVersion = packageJsonResult.success
33
88
  ? packageJsonResult.packageJson?.version || '0.6.5'
@@ -397,21 +452,24 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
397
452
  },
398
453
  {
399
454
  name: 'get-screen-generation-context',
400
- description: '[WORKFLOW STEP 1/3] Get complete context for AI agents to generate screen definitions from natural language.\n\n' +
455
+ description: '[WORKFLOW STEP 1/3] Get complete context for AI agents to generate screens from natural language for web or React Native direct-write workflows.\n\n' +
401
456
  'THIS IS THE FIRST STEP in the screen generation workflow:\n' +
402
- "1. Call THIS TOOL with user's description (Step 1/3)\n" +
403
- '2. Write Screen Definition JSON, then validate with validate-screen-definition (Step 2/3)\n' +
404
- '3. Call validate-environment if path known (Step 3/3)\n\n' +
405
- 'IMPORTANT: After validation passes, the AI agent writes React code DIRECTLY\n' +
406
- 'using the components and props provided in the context response.\n\n' +
457
+ '1. If project path is known, call detect-project-context once to set defaults\n' +
458
+ "2. Call THIS TOOL with user's description and optional platform override (Step 1/3)\n" +
459
+ '3. For web: write Screen Definition JSON, then validate with validate-screen-definition (Step 2/3)\n' +
460
+ '4. For React Native: write code directly from the returned direct-write contract\n' +
461
+ '5. Call validate-environment if path known (final step)\n\n' +
462
+ 'IMPORTANT: Web uses the validated Screen Definition path. React Native uses the direct-write contract path.\n\n' +
407
463
  'WHEN TO CALL:\n' +
408
464
  '- When user requests a new screen/page/component\n' +
409
465
  '- Before attempting to generate a Screen Definition JSON\n' +
466
+ '- Before writing Expo / React Native screens directly\n' +
410
467
  '- When you need to know available components, templates, or layout tokens\n\n' +
411
468
  'RETURNS:\n' +
412
469
  '- Template matches based on description\n' +
413
- '- Available components with usage examples\n' +
414
- '- JSON schema for Screen Definition\n' +
470
+ '- Available components with platform-aware guidance\n' +
471
+ '- JSON schema for Screen Definition (web path)\n' +
472
+ '- Direct-write runtime guidance (React Native path)\n' +
415
473
  '- Example definitions for reference\n' +
416
474
  '- Theme recipes and contextual hints',
417
475
  inputSchema: {
@@ -432,6 +490,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
432
490
  type: 'boolean',
433
491
  description: 'Include example screen definitions (default: true)',
434
492
  },
493
+ platform: {
494
+ type: 'string',
495
+ enum: ['web', 'react-native'],
496
+ description: 'Optional explicit platform override. If omitted, the session default or legacy web fallback is used.',
497
+ },
435
498
  },
436
499
  required: ['description'],
437
500
  },
@@ -519,30 +582,64 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
519
582
  required: ['screenDefinition', 'outputFormat'],
520
583
  },
521
584
  },
585
+ {
586
+ name: 'detect-project-context',
587
+ description: 'Detect the active project context from a filesystem path and optionally set it as the session default.\n\n' +
588
+ 'WHEN TO CALL:\n' +
589
+ '- As the first step when the user has provided a project path\n' +
590
+ '- Before using React Native direct-write workflow without repeating platform flags\n' +
591
+ '- To determine whether the target project is web, Expo, or bare React Native\n\n' +
592
+ 'RETURNS:\n' +
593
+ '- platform: web or react-native\n' +
594
+ '- environment: runtime and package manager\n' +
595
+ '- recommendations: default workflow recommendation for the detected project\n' +
596
+ '- sessionDefaultApplied: whether the detected context was stored for later tool calls',
597
+ inputSchema: {
598
+ type: 'object',
599
+ properties: {
600
+ projectPath: {
601
+ type: 'string',
602
+ description: 'Path to package.json or project root directory',
603
+ },
604
+ setAsDefault: {
605
+ type: 'boolean',
606
+ description: 'Store the detected project context as the active session default',
607
+ },
608
+ },
609
+ required: ['projectPath'],
610
+ },
611
+ },
522
612
  {
523
613
  name: 'validate-environment',
524
- description: '[WORKFLOW STEP 3/3 - Optional] Validate user environment: NPM packages + Tailwind CSS configuration for @framingui/ui.\n\n' +
614
+ description: '[WORKFLOW STEP 3/3 - Optional] Validate user environment for web or React Native direct-write delivery.\n\n' +
525
615
  'WHEN TO CALL:\n' +
526
- '- After writing React code, to verify required packages are installed\n' +
616
+ '- After writing React or React Native code, to verify required packages are installed\n' +
527
617
  '- When user wants to check if their project has required packages\n' +
528
618
  '- Before running generated code to ensure all dependencies are available\n' +
529
- '- To verify Tailwind CSS is configured correctly for @framingui/ui components\n\n' +
619
+ '- To verify Tailwind CSS is configured correctly for @framingui/ui components on web\n' +
620
+ '- To audit React Native source files for hardcoded values or web-only patterns\n\n' +
530
621
  'RETURNS:\n' +
531
622
  '- installed: Packages already in package.json with versions\n' +
532
623
  '- missing: Packages that need to be installed\n' +
533
624
  '- installCommands: Ready-to-use install commands for npm/yarn/pnpm/bun\n' +
534
- '- tailwind: Tailwind CSS config validation (content paths, animate plugin / package)\n\n' +
625
+ '- environment: detected runtime and package manager\n' +
626
+ '- tailwind: Tailwind CSS config validation (web path)\n' +
627
+ '- sourceAudit: source-file QC findings for React Native direct-write paths\n\n' +
535
628
  'TAILWIND VALIDATION (checkTailwind=true by default):\n' +
536
- '- Checks if tailwind.config.{ts,js,mjs,cjs} exists for Tailwind v3 projects\n' +
537
- '- Verifies @framingui/ui content paths are included for Tailwind v3 projects\n' +
538
- '- Verifies tailwindcss-animate is available for FramingUI overlay animations\n' +
629
+ '- Checks if tailwind.config.{ts,js,mjs,cjs} exists\n' +
630
+ '- Verifies @framingui/ui content paths are included (prevents missing styles)\n' +
631
+ '- Verifies tailwindcss-animate plugin is configured (required for Dialog, Popover animations)\n' +
539
632
  '- Returns actionable issues[] and fixes[] for each problem found\n\n' +
633
+ 'REACT NATIVE VALIDATION:\n' +
634
+ '- Detects Expo vs bare React Native projects\n' +
635
+ '- Skips Tailwind by default when platform=react-native\n' +
636
+ '- Audits sourceFiles for raw color/spacing/radius literals and web-only patterns such as className\n\n' +
540
637
  'EXAMPLE WORKFLOW:\n' +
541
638
  '1. get-screen-generation-context → get component info and context\n' +
542
- '2. validate-screen-definition → validate Screen Definition JSON\n' +
543
- '3. Write React code using components from context\n' +
639
+ '2. For web: validate-screen-definition → validate Screen Definition JSON\n' +
640
+ '3. Write code using the returned contract path\n' +
544
641
  '4. Call THIS TOOL with projectPath + requiredPackages\n' +
545
- '5. Show user missing packages, install commands, AND any Tailwind config issues',
642
+ '5. Show user missing packages, install commands, and any Tailwind or source-audit issues',
546
643
  inputSchema: {
547
644
  type: 'object',
548
645
  properties: {
@@ -555,10 +652,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
555
652
  description: 'Array of package names to validate (e.g., ["framer-motion", "@radix-ui/react-slot"])',
556
653
  items: { type: 'string' },
557
654
  },
655
+ platform: {
656
+ type: 'string',
657
+ enum: ['web', 'react-native'],
658
+ description: 'Target platform for environment validation (default: web)',
659
+ },
558
660
  checkTailwind: {
559
661
  type: 'boolean',
560
662
  description: 'Also validate Tailwind CSS configuration for @framingui/ui compatibility (default: true)',
561
663
  },
664
+ sourceFiles: {
665
+ type: 'array',
666
+ description: 'Optional source files to audit for direct-write QC (recommended for React Native)',
667
+ items: { type: 'string' },
668
+ },
562
669
  },
563
670
  required: ['projectPath', 'requiredPackages'],
564
671
  },
@@ -572,22 +679,43 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
572
679
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
573
680
  const { name, arguments: args } = request.params;
574
681
  info(`CallTool request: ${name}`);
575
- // 모든 도구 호출 전에 인증 가드 실행
576
- try {
577
- requireAuth();
578
- }
579
- catch (e) {
580
- if (e instanceof AuthRequiredError) {
682
+ if (name !== 'detect-project-context') {
683
+ // 모든 원격/카탈로그 도구 호출 전에 인증 가드 실행
684
+ try {
685
+ requireAuth();
686
+ }
687
+ catch (e) {
688
+ if (e instanceof AuthRequiredError) {
689
+ meterToolCall(name, 'auth_error');
690
+ return {
691
+ content: [
692
+ {
693
+ type: 'text',
694
+ text: JSON.stringify({
695
+ success: false,
696
+ code: 'AUTH_REQUIRED',
697
+ error: 'Authentication required to use FramingUI MCP tools.',
698
+ nextAction: 'Run `framingui-mcp login` or set FRAMINGUI_API_KEY, then retry the same tool call.',
699
+ signupUrl: 'https://framingui.com/auth/signup?utm_source=mcp&utm_medium=cli&utm_campaign=auth_prompt',
700
+ retryable: true,
701
+ }),
702
+ },
703
+ ],
704
+ isError: true,
705
+ };
706
+ }
707
+ // 예상치 못한 예외는 로깅 후 에러 반환 (silent swallow 방지)
708
+ logError(`Unexpected error in requireAuth: ${e}`);
709
+ meterToolCall(name, 'auth_error');
581
710
  return {
582
711
  content: [
583
712
  {
584
713
  type: 'text',
585
714
  text: JSON.stringify({
586
715
  success: false,
587
- code: 'AUTH_REQUIRED',
588
- error: 'Authentication required to use FramingUI MCP tools.',
589
- nextAction: 'Run `framingui-mcp login` or set FRAMINGUI_API_KEY, then retry the same tool call.',
590
- signupUrl: 'https://framingui.com/auth/signup?utm_source=mcp&utm_medium=cli&utm_campaign=auth_prompt',
716
+ code: 'AUTH_CHECK_FAILED',
717
+ error: 'Authentication check failed unexpectedly.',
718
+ detail: e instanceof Error ? e.message : String(e),
591
719
  retryable: true,
592
720
  }),
593
721
  },
@@ -595,18 +723,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
595
723
  isError: true,
596
724
  };
597
725
  }
598
- // 예상치 못한 예외는 로깅 후 에러 반환 (silent swallow 방지)
599
- logError(`Unexpected error in requireAuth: ${e}`);
726
+ }
727
+ const quotaDecision = getQuotaPolicyDecision(name);
728
+ if (!quotaDecision.allowed) {
729
+ meterToolCall(name, 'validation_error');
600
730
  return {
601
731
  content: [
602
732
  {
603
733
  type: 'text',
604
734
  text: JSON.stringify({
605
735
  success: false,
606
- code: 'AUTH_CHECK_FAILED',
607
- error: 'Authentication check failed unexpectedly.',
608
- detail: e instanceof Error ? e.message : String(e),
609
- retryable: true,
736
+ code: 'QUOTA_EXCEEDED',
737
+ error: quotaDecision.warning ??
738
+ 'This tool call would exceed the included quota while hard cap mode is enabled.',
739
+ retryable: false,
610
740
  }),
611
741
  },
612
742
  ],
@@ -617,223 +747,111 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
617
747
  switch (name) {
618
748
  case 'whoami': {
619
749
  WhoamiInputSchema.parse(args);
620
- const result = await whoamiTool();
621
- return {
622
- content: [
623
- {
624
- type: 'text',
625
- text: JSON.stringify(result, null, 2),
626
- },
627
- ],
628
- };
750
+ const result = attachQuotaWarning(await whoamiTool(), quotaDecision.warning);
751
+ meterToolCall(name, inferUsageOutcome(result));
752
+ return buildToolResponse(result);
629
753
  }
630
754
  case 'generate-blueprint': {
631
- // Validate input
632
755
  const validatedInput = GenerateBlueprintInputSchema.parse(args);
633
- const result = await generateBlueprintTool(validatedInput);
634
- return {
635
- content: [
636
- {
637
- type: 'text',
638
- text: JSON.stringify(result, null, 2),
639
- },
640
- ],
641
- };
756
+ const result = attachQuotaWarning(await generateBlueprintTool(validatedInput), quotaDecision.warning);
757
+ meterToolCall(name, inferUsageOutcome(result));
758
+ return buildToolResponse(result);
642
759
  }
643
760
  case 'list-icon-libraries': {
644
- // Validate input (no required fields)
645
761
  ListIconLibrariesInputSchema.parse(args);
646
- const result = await listIconLibrariesTool();
647
- return {
648
- content: [
649
- {
650
- type: 'text',
651
- text: JSON.stringify(result, null, 2),
652
- },
653
- ],
654
- };
762
+ const result = attachQuotaWarning(await listIconLibrariesTool(), quotaDecision.warning);
763
+ meterToolCall(name, inferUsageOutcome(result));
764
+ return buildToolResponse(result);
655
765
  }
656
766
  case 'preview-icon-library': {
657
- // Validate input
658
767
  const validatedInput = PreviewIconLibraryInputSchema.parse(args);
659
- const result = await previewIconLibraryTool(validatedInput);
660
- return {
661
- content: [
662
- {
663
- type: 'text',
664
- text: JSON.stringify(result, null, 2),
665
- },
666
- ],
667
- };
768
+ const result = attachQuotaWarning(await previewIconLibraryTool(validatedInput), quotaDecision.warning);
769
+ meterToolCall(name, inferUsageOutcome(result));
770
+ return buildToolResponse(result);
668
771
  }
669
772
  case 'list-themes': {
670
- // Validate input (no required fields)
671
773
  ListThemesInputSchema.parse(args);
672
- const result = await listThemesTool();
673
- return {
674
- content: [
675
- {
676
- type: 'text',
677
- text: JSON.stringify(result, null, 2),
678
- },
679
- ],
680
- };
774
+ const result = attachQuotaWarning(await listThemesTool(), quotaDecision.warning);
775
+ meterToolCall(name, inferUsageOutcome(result));
776
+ return buildToolResponse(result);
681
777
  }
682
778
  case 'preview-theme': {
683
- // Validate input
684
779
  const validatedInput = PreviewThemeInputSchema.parse(args);
685
- const result = await previewThemeTool(validatedInput);
686
- return {
687
- content: [
688
- {
689
- type: 'text',
690
- text: JSON.stringify(result, null, 2),
691
- },
692
- ],
693
- };
780
+ const result = attachQuotaWarning(await previewThemeTool(validatedInput), quotaDecision.warning);
781
+ meterToolCall(name, inferUsageOutcome(result));
782
+ return buildToolResponse(result);
694
783
  }
695
784
  case 'export-screen': {
696
- // Validate input
697
785
  const validatedInput = ExportScreenInputSchema.parse(args);
698
- const result = await exportScreenTool(validatedInput);
699
- return {
700
- content: [
701
- {
702
- type: 'text',
703
- text: JSON.stringify(result, null, 2),
704
- },
705
- ],
706
- };
786
+ const result = attachQuotaWarning(await exportScreenTool(validatedInput), quotaDecision.warning);
787
+ meterToolCall(name, inferUsageOutcome(result));
788
+ return buildToolResponse(result);
707
789
  }
708
790
  case 'validate_screen': {
709
- // Validate input
710
791
  const validatedInput = ValidateScreenInputSchema.parse(args);
711
- const result = await validateScreenTool(validatedInput);
712
- return {
713
- content: [
714
- {
715
- type: 'text',
716
- text: JSON.stringify(result, null, 2),
717
- },
718
- ],
719
- };
792
+ const result = attachQuotaWarning(await validateScreenTool(validatedInput), quotaDecision.warning);
793
+ meterToolCall(name, inferUsageOutcome(result));
794
+ return buildToolResponse(result);
720
795
  }
721
796
  case 'list_tokens': {
722
- // Validate input
723
797
  const validatedInput = ListTokensInputSchema.parse(args);
724
- const result = await listTokensTool(validatedInput);
725
- return {
726
- content: [
727
- {
728
- type: 'text',
729
- text: JSON.stringify(result, null, 2),
730
- },
731
- ],
732
- };
798
+ const result = attachQuotaWarning(await listTokensTool(validatedInput), quotaDecision.warning);
799
+ meterToolCall(name, inferUsageOutcome(result));
800
+ return buildToolResponse(result);
733
801
  }
734
802
  case 'list-components': {
735
- // Validate input
736
803
  const validatedInput = ListComponentsInputSchema.parse(args);
737
- const result = await listComponentsTool(validatedInput);
738
- return {
739
- content: [
740
- {
741
- type: 'text',
742
- text: JSON.stringify(result, null, 2),
743
- },
744
- ],
745
- };
804
+ const result = attachQuotaWarning(await listComponentsTool(validatedInput), quotaDecision.warning);
805
+ meterToolCall(name, inferUsageOutcome(result));
806
+ return buildToolResponse(result);
746
807
  }
747
808
  case 'preview-component': {
748
- // Validate input
749
809
  const validatedInput = PreviewComponentInputSchema.parse(args);
750
- const result = await previewComponentTool(validatedInput);
751
- return {
752
- content: [
753
- {
754
- type: 'text',
755
- text: JSON.stringify(result, null, 2),
756
- },
757
- ],
758
- };
810
+ const result = attachQuotaWarning(await previewComponentTool(validatedInput), quotaDecision.warning);
811
+ meterToolCall(name, inferUsageOutcome(result));
812
+ return buildToolResponse(result);
759
813
  }
760
814
  case 'list-screen-templates': {
761
- // Validate input
762
815
  const validatedInput = ListScreenTemplatesInputSchema.parse(args);
763
- const result = await listScreenTemplatesTool(validatedInput);
764
- return {
765
- content: [
766
- {
767
- type: 'text',
768
- text: JSON.stringify(result, null, 2),
769
- },
770
- ],
771
- };
816
+ const result = attachQuotaWarning(await listScreenTemplatesTool(validatedInput), quotaDecision.warning);
817
+ meterToolCall(name, inferUsageOutcome(result));
818
+ return buildToolResponse(result);
772
819
  }
773
820
  case 'preview-screen-template': {
774
- // Validate input
775
821
  const validatedInput = PreviewScreenTemplateInputSchema.parse(args);
776
- const result = await previewScreenTemplateTool(validatedInput);
777
- return {
778
- content: [
779
- {
780
- type: 'text',
781
- text: JSON.stringify(result, null, 2),
782
- },
783
- ],
784
- };
822
+ const result = attachQuotaWarning(await previewScreenTemplateTool(validatedInput), quotaDecision.warning);
823
+ meterToolCall(name, inferUsageOutcome(result));
824
+ return buildToolResponse(result);
785
825
  }
786
826
  case 'get-screen-generation-context': {
787
- // Validate input
788
827
  const validatedInput = GetScreenGenerationContextInputSchema.parse(args);
789
- const result = await getScreenGenerationContextTool(validatedInput);
790
- return {
791
- content: [
792
- {
793
- type: 'text',
794
- text: JSON.stringify(result, null, 2),
795
- },
796
- ],
797
- };
828
+ const result = attachQuotaWarning(await getScreenGenerationContextTool(validatedInput), quotaDecision.warning);
829
+ meterToolCall(name, inferUsageOutcome(result));
830
+ return buildToolResponse(result);
798
831
  }
799
832
  case 'validate-screen-definition': {
800
- // Validate input
801
833
  const validatedInput = ValidateScreenDefinitionInputSchema.parse(args);
802
- const result = await validateScreenDefinitionTool(validatedInput);
803
- return {
804
- content: [
805
- {
806
- type: 'text',
807
- text: JSON.stringify(result, null, 2),
808
- },
809
- ],
810
- };
834
+ const result = attachQuotaWarning(await validateScreenDefinitionTool(validatedInput), quotaDecision.warning);
835
+ meterToolCall(name, inferUsageOutcome(result));
836
+ return buildToolResponse(result);
811
837
  }
812
838
  case 'generate_screen': {
813
- // Validate input
814
839
  const validatedInput = GenerateScreenInputSchema.parse(args);
815
- const result = await generateScreenTool(validatedInput);
816
- return {
817
- content: [
818
- {
819
- type: 'text',
820
- text: JSON.stringify(result, null, 2),
821
- },
822
- ],
823
- };
840
+ const result = attachQuotaWarning(await generateScreenTool(validatedInput), quotaDecision.warning);
841
+ meterToolCall(name, inferUsageOutcome(result));
842
+ return buildToolResponse(result);
824
843
  }
825
844
  case 'validate-environment': {
826
- // Validate input
827
845
  const validatedInput = ValidateEnvironmentInputSchema.parse(args);
828
- const result = await validateEnvironmentTool(validatedInput);
829
- return {
830
- content: [
831
- {
832
- type: 'text',
833
- text: JSON.stringify(result, null, 2),
834
- },
835
- ],
836
- };
846
+ const result = attachQuotaWarning(await validateEnvironmentTool(validatedInput), quotaDecision.warning);
847
+ meterToolCall(name, inferUsageOutcome(result));
848
+ return buildToolResponse(result);
849
+ }
850
+ case 'detect-project-context': {
851
+ const validatedInput = DetectProjectContextInputSchema.parse(args);
852
+ const result = await detectProjectContextTool(validatedInput);
853
+ meterToolCall(name, inferUsageOutcome(result));
854
+ return buildToolResponse(result);
837
855
  }
838
856
  default:
839
857
  throw new Error(`Unknown tool: ${name}`);
@@ -841,6 +859,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
841
859
  }
842
860
  catch (error) {
843
861
  logError(`Tool execution error: ${error}`);
862
+ const outcome = error instanceof Error && error.name === 'ZodError' ? 'validation_error' : 'runtime_error';
863
+ meterToolCall(name, outcome);
844
864
  return {
845
865
  content: [
846
866
  {
@@ -921,5 +941,5 @@ catch (err) {
921
941
  logError(`Failed to connect to stdio transport: ${errorMessage}`);
922
942
  process.exit(1);
923
943
  }
924
- info('17 MCP tools registered: whoami, generate-blueprint, list-themes, preview-theme, list-icon-libraries, preview-icon-library, export-screen, validate_screen, list_tokens, list-components, preview-component, list-screen-templates, preview-screen-template, get-screen-generation-context, validate-screen-definition, generate_screen, validate-environment');
944
+ info('18 MCP tools registered: whoami, generate-blueprint, list-themes, preview-theme, list-icon-libraries, preview-icon-library, export-screen, validate_screen, list_tokens, list-components, preview-component, list-screen-templates, preview-screen-template, get-screen-generation-context, validate-screen-definition, generate_screen, validate-environment, detect-project-context');
925
945
  //# sourceMappingURL=index.js.map