@seed-ship/mcp-ui-solid 5.5.1 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/components/FormRenderer.cjs +13 -2
  3. package/dist/components/FormRenderer.cjs.map +1 -1
  4. package/dist/components/FormRenderer.d.ts.map +1 -1
  5. package/dist/components/FormRenderer.js +13 -2
  6. package/dist/components/FormRenderer.js.map +1 -1
  7. package/dist/components/GenerativeUIErrorBoundary.cjs +11 -0
  8. package/dist/components/GenerativeUIErrorBoundary.cjs.map +1 -1
  9. package/dist/components/GenerativeUIErrorBoundary.d.ts.map +1 -1
  10. package/dist/components/GenerativeUIErrorBoundary.js +11 -0
  11. package/dist/components/GenerativeUIErrorBoundary.js.map +1 -1
  12. package/dist/components/StreamingUIRenderer.cjs +49 -3
  13. package/dist/components/StreamingUIRenderer.cjs.map +1 -1
  14. package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
  15. package/dist/components/StreamingUIRenderer.js +51 -5
  16. package/dist/components/StreamingUIRenderer.js.map +1 -1
  17. package/dist/components/UIResourceRenderer.cjs +62 -3
  18. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  19. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  20. package/dist/components/UIResourceRenderer.js +64 -5
  21. package/dist/components/UIResourceRenderer.js.map +1 -1
  22. package/dist/context/MCPUITelemetryContext.cjs +25 -0
  23. package/dist/context/MCPUITelemetryContext.cjs.map +1 -0
  24. package/dist/context/MCPUITelemetryContext.d.ts +36 -0
  25. package/dist/context/MCPUITelemetryContext.d.ts.map +1 -0
  26. package/dist/context/MCPUITelemetryContext.js +25 -0
  27. package/dist/context/MCPUITelemetryContext.js.map +1 -0
  28. package/dist/index.cjs +6 -0
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +4 -0
  31. package/dist/index.d.ts +4 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +6 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/mcp-ui-spec/dist/schemas.cjs +16 -5
  36. package/dist/mcp-ui-spec/dist/schemas.cjs.map +1 -1
  37. package/dist/mcp-ui-spec/dist/schemas.js +16 -5
  38. package/dist/mcp-ui-spec/dist/schemas.js.map +1 -1
  39. package/dist/services/telemetry.cjs +56 -0
  40. package/dist/services/telemetry.cjs.map +1 -0
  41. package/dist/services/telemetry.d.ts +87 -0
  42. package/dist/services/telemetry.d.ts.map +1 -0
  43. package/dist/services/telemetry.js +56 -0
  44. package/dist/services/telemetry.js.map +1 -0
  45. package/dist/services/validation.cjs +25 -24
  46. package/dist/services/validation.cjs.map +1 -1
  47. package/dist/services/validation.d.ts.map +1 -1
  48. package/dist/services/validation.js +26 -25
  49. package/dist/services/validation.js.map +1 -1
  50. package/package.json +2 -2
  51. package/src/components/FormRenderer.tsx +14 -0
  52. package/src/components/GenerativeUIErrorBoundary.tsx +17 -1
  53. package/src/components/StreamingUIRenderer.tsx +55 -3
  54. package/src/components/UIResourceRenderer.tsx +79 -3
  55. package/src/context/MCPUITelemetryContext.test.tsx +119 -0
  56. package/src/context/MCPUITelemetryContext.tsx +71 -0
  57. package/src/index.ts +15 -0
  58. package/src/services/telemetry.test.ts +134 -0
  59. package/src/services/telemetry.ts +149 -0
  60. package/src/services/validation.ts +43 -41
  61. package/tsconfig.tsbuildinfo +1 -1
@@ -22,6 +22,9 @@ import {
22
22
  ImageGalleryParamsSchema,
23
23
  ActionGroupParamsSchema,
24
24
  CodeComponentParamsSchema,
25
+ // v5.6.0 — added after spec@5.0.2 relaxations (deposium audit §M)
26
+ MapComponentParamsSchema,
27
+ FormComponentParamsSchema,
25
28
  } from '@seed-ship/mcp-ui-spec'
26
29
  import type {
27
30
  UIComponent,
@@ -47,7 +50,7 @@ const KNOWN_COMPONENT_TYPES: Set<string> = new Set<ComponentType>([
47
50
  ])
48
51
 
49
52
  /**
50
- * Spec-driven validation dispatch table (B.1 — v5.5.0).
53
+ * Spec-driven validation dispatch table (B.1 — v5.5.0, expanded in v5.6.0).
51
54
  *
52
55
  * For each ComponentType where we delegate shape validation to a Zod schema
53
56
  * from `@seed-ship/mcp-ui-spec`, this table maps:
@@ -56,13 +59,13 @@ const KNOWN_COMPONENT_TYPES: Set<string> = new Set<ComponentType>([
56
59
  * pre-v5.5.0 `errors[].code` API contract — see MCP-UI-AUDIT-2026-04-26.md
57
60
  * §I.3.a + §J.1)
58
61
  *
62
+ * **v5.6.0** : `map` and `form` joined the dispatch after spec@5.0.2 relaxed
63
+ * their schemas (LatLngPoint union for map.center, regex relax for
64
+ * field.name) per deposium audit §L answers. Closed B.1 to **14/17 types**.
65
+ *
59
66
  * Types deliberately omitted (kept on the imperative path):
60
67
  * - `chart`, `table` — have rich imperative validators with their own
61
68
  * codes (MISSING_DATA, DATA_LENGTH_MISMATCH, RESOURCE_LIMIT_EXCEEDED, …)
62
- * - `form` — spec FormFieldSchema has strict regex on field
63
- * names that could reject LLM-generated payloads. Conservative.
64
- * - `map` — spec center is `tuple([number, number])`; production
65
- * payloads use `{lat, lng}` objects. Avoid backward-compat regression.
66
69
  * - `modal` — all params are optional; nothing to enforce.
67
70
  * - `grid`, `footer`, `composite` — pass-through, validated elsewhere.
68
71
  */
@@ -79,6 +82,9 @@ const SPEC_VALIDATORS: Partial<Record<ComponentType, { schema: ZodSchema; legacy
79
82
  'action-group': { schema: ActionGroupParamsSchema, legacyCode: 'EMPTY_ACTION_GROUP' },
80
83
  code: { schema: CodeComponentParamsSchema, legacyCode: 'INVALID_CODE' },
81
84
  artifact: { schema: ArtifactComponentParamsSchema, legacyCode: 'INVALID_ARTIFACT' },
85
+ // v5.6.0 additions
86
+ form: { schema: FormComponentParamsSchema, legacyCode: 'EMPTY_FORM' },
87
+ map: { schema: MapComponentParamsSchema, legacyCode: 'INVALID_MAP' },
82
88
  }
83
89
 
84
90
  /**
@@ -677,35 +683,49 @@ export function validateComponent(
677
683
  errors.push(...(sizeResult.errors || []))
678
684
  }
679
685
 
680
- // Type-specific validation (B.1 — v5.5.0).
686
+ // Type-specific validation (B.1 — v5.5.0, expanded v5.6.0).
681
687
  //
682
- // 12 types delegate shape validation to Zod schemas in `mcp-ui-spec` via
683
- // SPEC_VALIDATORS. The 5 remaining types stay imperative because they
684
- // need cross-field consistency, resource limits, or backward-compat logic
685
- // that pure Zod can't express without `.refine()` (see SPEC_VALIDATORS docstring).
688
+ // 14 types delegate shape validation to Zod schemas in `mcp-ui-spec` via
689
+ // SPEC_VALIDATORS. The 3 remaining types stay imperative because they
690
+ // need cross-field consistency, resource limits, or have nothing to validate
691
+ // (see SPEC_VALIDATORS docstring).
686
692
  const specValidator = SPEC_VALIDATORS[component.type]
687
693
  if (specValidator) {
688
694
  const result = specValidator.schema.safeParse(component.params)
689
695
  if (!result.success) {
690
696
  errors.push(...mapZodIssuesToErrors(result.error.issues, specValidator.legacyCode))
691
697
  }
692
- // Iframe + video: chain the domain whitelist post-check ONLY when the
693
- // shape parse succeeded (i.e. url is present and a string). Skipping the
694
- // domain check when shape failed avoids cascading errors on the same field.
695
- if (result.success && (component.type === 'iframe' || component.type === 'video')) {
696
- const url = (component.params as { url?: string })?.url
697
- if (typeof url === 'string') {
698
- const domainResult = validateIframeDomain(url, {
699
- policy: options?.iframePolicy,
700
- customDomains: options?.customIframeDomains,
701
- })
702
- if (!domainResult.valid) {
703
- errors.push(...(domainResult.errors || []))
698
+ // Post-spec chained checks. Skipped when the shape parse failed to avoid
699
+ // cascading errors on already-broken payloads.
700
+ if (result.success) {
701
+ // Iframe + video: domain whitelist
702
+ if (component.type === 'iframe' || component.type === 'video') {
703
+ const url = (component.params as { url?: string })?.url
704
+ if (typeof url === 'string') {
705
+ const domainResult = validateIframeDomain(url, {
706
+ policy: options?.iframePolicy,
707
+ customDomains: options?.customIframeDomains,
708
+ })
709
+ if (!domainResult.valid) {
710
+ errors.push(...(domainResult.errors || []))
711
+ }
712
+ }
713
+ }
714
+ // Map (v5.6.0): center OR markers required. Spec has both .optional()
715
+ // since auto-center from markers is supported, but we need ONE of them.
716
+ if (component.type === 'map') {
717
+ const mapParams = component.params as { center?: unknown; markers?: unknown[] }
718
+ if (!mapParams.center && (!Array.isArray(mapParams.markers) || mapParams.markers.length === 0)) {
719
+ errors.push({
720
+ path: 'params',
721
+ message: 'Map must have center or markers',
722
+ code: 'INVALID_MAP',
723
+ })
704
724
  }
705
725
  }
706
726
  }
707
727
  } else {
708
- // Imperative path for chart/table/form/map/modal/grid/footer/composite.
728
+ // Imperative path for chart/table/modal/grid/footer/composite.
709
729
  switch (component.type) {
710
730
  case 'chart': {
711
731
  const chartResult = validateChartComponent(component.params as ChartComponentParams, limits)
@@ -723,24 +743,6 @@ export function validateComponent(
723
743
  break
724
744
  }
725
745
 
726
- case 'form': {
727
- const formParams = component.params as { fields?: unknown[] }
728
- if (!Array.isArray(formParams.fields) || formParams.fields.length === 0) {
729
- errors.push({ path: 'params.fields', message: 'Form must have non-empty fields array', code: 'EMPTY_FORM' })
730
- }
731
- break
732
- }
733
-
734
- case 'map': {
735
- // Map can auto-detect center from markers, so center is not strictly required.
736
- // Spec MapComponentParamsSchema would be too strict (tuple-only center) — kept imperative.
737
- const mapParams = component.params as { center?: unknown; markers?: unknown[] }
738
- if (!mapParams.center && (!Array.isArray(mapParams.markers) || mapParams.markers.length === 0)) {
739
- errors.push({ path: 'params', message: 'Map must have center or markers', code: 'INVALID_MAP' })
740
- }
741
- break
742
- }
743
-
744
746
  case 'modal':
745
747
  // Modal is valid with minimal params (title optional, content can be children).
746
748
  break