semiotic 3.7.4 → 3.8.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 (517) hide show
  1. package/CLAUDE.md +60 -15
  2. package/README.md +135 -24
  3. package/agent-skill/semiotic-charts/SKILL.md +146 -0
  4. package/ai/cli.js +133 -45
  5. package/ai/componentMetadata.cjs +6 -1
  6. package/ai/dist/mcp-server.js +1651 -154
  7. package/ai/examples.md +174 -0
  8. package/ai/schema.json +2934 -517
  9. package/ai/surface-manifest.json +200 -0
  10. package/ai/system-prompt.md +19 -3
  11. package/dist/components/Annotation.d.ts +4 -0
  12. package/dist/components/CategoryColors.d.ts +1 -1
  13. package/dist/components/ChartContainer.d.ts +91 -0
  14. package/dist/components/ChartGrid.d.ts +11 -1
  15. package/dist/components/CircularBrush.d.ts +61 -0
  16. package/dist/components/ContextLayout.d.ts +5 -1
  17. package/dist/components/DataSummaryContext.d.ts +1 -0
  18. package/dist/components/DirectManipulationControl.d.ts +63 -0
  19. package/dist/components/LinkedCharts.d.ts +3 -1
  20. package/dist/components/MobileAnnotationCalloutList.d.ts +13 -0
  21. package/dist/components/MobileChartContainer.d.ts +51 -0
  22. package/dist/components/MobileStandardControls.d.ts +68 -0
  23. package/dist/components/SmallMultipleChart.d.ts +84 -0
  24. package/dist/components/Tooltip/FlippingTooltip.d.ts +21 -0
  25. package/dist/components/ai/ChartRecipe.d.ts +16 -0
  26. package/dist/components/ai/IntentMark.d.ts +15 -0
  27. package/dist/components/ai/chartCapabilities.d.ts +8 -1
  28. package/dist/components/ai/chartCapabilityTypes.d.ts +42 -1
  29. package/dist/components/ai/chartClinic.d.ts +91 -0
  30. package/dist/components/ai/chartRecipeRegistry.d.ts +14 -0
  31. package/dist/components/ai/chartRecipes.d.ts +278 -0
  32. package/dist/components/ai/dataPitfallsBridge.d.ts +203 -0
  33. package/dist/components/ai/dataQualityBridge.d.ts +201 -0
  34. package/dist/components/ai/describeChart.d.ts +9 -0
  35. package/dist/components/ai/describeRecipeChart.d.ts +16 -0
  36. package/dist/components/ai/generativeChart.d.ts +170 -0
  37. package/dist/components/ai/intentManifest.d.ts +62 -0
  38. package/dist/components/ai/navigationTree.d.ts +3 -0
  39. package/dist/components/ai/observedSceneAudit.d.ts +72 -0
  40. package/dist/components/ai/readerGrounding.d.ts +83 -0
  41. package/dist/components/ai/recipeCapability.d.ts +8 -0
  42. package/dist/components/ai/recipeNavigation.d.ts +13 -0
  43. package/dist/components/ai/recipeSemantics.d.ts +11 -0
  44. package/dist/components/ai/repairChartConfig.d.ts +9 -0
  45. package/dist/components/ai/suggestCharts.d.ts +11 -1
  46. package/dist/components/charts/custom/GeoCustomChart.d.ts +48 -0
  47. package/dist/components/charts/custom/NetworkCustomChart.d.ts +22 -3
  48. package/dist/components/charts/custom/OrdinalCustomChart.d.ts +8 -2
  49. package/dist/components/charts/custom/XYCustomChart.d.ts +11 -2
  50. package/dist/components/charts/geo/DistanceCartogram.d.ts +1 -1
  51. package/dist/components/charts/geo/FlowMap.d.ts +1 -1
  52. package/dist/components/charts/geo/ProportionalSymbolMap.d.ts +1 -1
  53. package/dist/components/charts/geo/index.d.ts +2 -0
  54. package/dist/components/charts/index.d.ts +29 -1
  55. package/dist/components/charts/network/ChordDiagram.d.ts +1 -1
  56. package/dist/components/charts/network/CirclePack.d.ts +1 -1
  57. package/dist/components/charts/network/ForceDirectedGraph.d.ts +15 -5
  58. package/dist/components/charts/network/OrbitDiagram.d.ts +1 -1
  59. package/dist/components/charts/network/ProcessSankey.d.ts +1 -1
  60. package/dist/components/charts/network/SankeyDiagram.d.ts +1 -1
  61. package/dist/components/charts/network/TreeDiagram.d.ts +1 -1
  62. package/dist/components/charts/network/Treemap.d.ts +1 -1
  63. package/dist/components/charts/network/useForceLayout.d.ts +17 -0
  64. package/dist/components/charts/ordinal/BarChart.d.ts +1 -1
  65. package/dist/components/charts/ordinal/BoxPlot.d.ts +1 -1
  66. package/dist/components/charts/ordinal/DonutChart.d.ts +1 -1
  67. package/dist/components/charts/ordinal/DotPlot.d.ts +1 -1
  68. package/dist/components/charts/ordinal/FunnelChart.d.ts +1 -1
  69. package/dist/components/charts/ordinal/GroupedBarChart.d.ts +1 -1
  70. package/dist/components/charts/ordinal/Histogram.d.ts +1 -1
  71. package/dist/components/charts/ordinal/PieChart.d.ts +1 -1
  72. package/dist/components/charts/ordinal/RidgelinePlot.d.ts +1 -1
  73. package/dist/components/charts/ordinal/StackedBarChart.d.ts +1 -1
  74. package/dist/components/charts/ordinal/SwarmPlot.d.ts +8 -1
  75. package/dist/components/charts/ordinal/SwimlaneChart.d.ts +1 -1
  76. package/dist/components/charts/ordinal/ViolinPlot.d.ts +1 -1
  77. package/dist/components/charts/physics/ChainReactionChart.d.ts +9 -0
  78. package/dist/components/charts/physics/CollisionSwarmChart.capability.d.ts +2 -0
  79. package/dist/components/charts/physics/CollisionSwarmChart.d.ts +47 -0
  80. package/dist/components/charts/physics/EventDropChart.capability.d.ts +2 -0
  81. package/dist/components/charts/physics/EventDropChart.d.ts +53 -0
  82. package/dist/components/charts/physics/GaltonBoardChart.capability.d.ts +2 -0
  83. package/dist/components/charts/physics/GaltonBoardChart.d.ts +66 -0
  84. package/dist/components/charts/physics/GauntletChart.capability.d.ts +2 -0
  85. package/dist/components/charts/physics/GauntletChart.d.ts +73 -0
  86. package/dist/components/charts/physics/PhysicalFlowChart.capability.d.ts +2 -0
  87. package/dist/components/charts/physics/PhysicalFlowChart.d.ts +63 -0
  88. package/dist/components/charts/physics/PhysicsCustomChart.d.ts +120 -0
  89. package/dist/components/charts/physics/PhysicsPileChart.capability.d.ts +2 -0
  90. package/dist/components/charts/physics/PhysicsPileChart.d.ts +53 -0
  91. package/dist/components/charts/physics/ProcessFlowChart.capability.d.ts +2 -0
  92. package/dist/components/charts/physics/ProcessFlowChart.d.ts +96 -0
  93. package/dist/components/charts/physics/chainReactionOverlay.d.ts +22 -0
  94. package/dist/components/charts/physics/chainReactionRuntime.d.ts +37 -0
  95. package/dist/components/charts/physics/chainReactionTypes.d.ts +97 -0
  96. package/dist/components/charts/physics/collisionSwarmPhysics.d.ts +27 -0
  97. package/dist/components/charts/physics/dependencyMachine.d.ts +188 -0
  98. package/dist/components/charts/physics/eventDropPhysics.d.ts +64 -0
  99. package/dist/components/charts/physics/galtonBoardPhysics.d.ts +28 -0
  100. package/dist/components/charts/physics/gauntletChartProps.d.ts +67 -0
  101. package/dist/components/charts/physics/gauntletChrome.d.ts +33 -0
  102. package/dist/components/charts/physics/gauntletController.d.ts +73 -0
  103. package/dist/components/charts/physics/gauntletEffects.d.ts +36 -0
  104. package/dist/components/charts/physics/gauntletPhysics.d.ts +71 -0
  105. package/dist/components/charts/physics/gauntletRuntime.d.ts +42 -0
  106. package/dist/components/charts/physics/gauntletTypes.d.ts +213 -0
  107. package/dist/components/charts/physics/physicalFlowPhysics.d.ts +57 -0
  108. package/dist/components/charts/physics/physicsChartShared.d.ts +41 -0
  109. package/dist/components/charts/physics/physicsChartUtils.d.ts +18 -0
  110. package/dist/components/charts/physics/physicsEncoding.d.ts +142 -0
  111. package/dist/components/charts/physics/physicsHocHandle.d.ts +53 -0
  112. package/dist/components/charts/physics/physicsHocUtils.d.ts +128 -0
  113. package/dist/components/charts/physics/physicsPilePhysics.d.ts +41 -0
  114. package/dist/components/charts/physics/physicsProcessPrimitives.d.ts +38 -0
  115. package/dist/components/charts/physics/processFlowPhysics.d.ts +97 -0
  116. package/dist/components/charts/realtime/RealtimeHeatmap.d.ts +9 -1
  117. package/dist/components/charts/realtime/RealtimeHistogram.d.ts +9 -1
  118. package/dist/components/charts/realtime/RealtimeLineChart.d.ts +30 -1
  119. package/dist/components/charts/realtime/RealtimeSwarmChart.d.ts +16 -2
  120. package/dist/components/charts/realtime/RealtimeWaterfallChart.d.ts +9 -1
  121. package/dist/components/charts/realtime/aggregate.d.ts +51 -0
  122. package/dist/components/charts/realtime/eventTime.d.ts +26 -0
  123. package/dist/components/charts/shared/auditMobileVisualization.d.ts +91 -0
  124. package/dist/components/charts/shared/chartDefinitionPilot.d.ts +145 -0
  125. package/dist/components/charts/shared/chartFamilySets.d.ts +20 -0
  126. package/dist/components/charts/shared/chartSpecCore.d.ts +165 -0
  127. package/dist/components/charts/shared/chartSpecs.d.ts +8 -127
  128. package/dist/components/charts/shared/chartSpecsGeo.d.ts +2 -0
  129. package/dist/components/charts/shared/chartSpecsNetwork.d.ts +2 -0
  130. package/dist/components/charts/shared/chartSpecsOrdinal.d.ts +2 -0
  131. package/dist/components/charts/shared/chartSpecsPhysics.d.ts +2 -0
  132. package/dist/components/charts/shared/chartSpecsRealtime.d.ts +2 -0
  133. package/dist/components/charts/shared/chartSpecsValue.d.ts +2 -0
  134. package/dist/components/charts/shared/chartSpecsXY.d.ts +2 -0
  135. package/dist/components/charts/shared/colorContrast.d.ts +3 -0
  136. package/dist/components/charts/shared/colorPalettes.d.ts +2 -0
  137. package/dist/components/charts/shared/colorUtils.d.ts +17 -13
  138. package/dist/components/charts/shared/diagnoseAnnotationChecks.d.ts +4 -0
  139. package/dist/components/charts/shared/diagnoseConfig.d.ts +9 -14
  140. package/dist/components/charts/shared/diagnoseMisleadingChecks.d.ts +9 -0
  141. package/dist/components/charts/shared/diagnosePhysicsChecks.d.ts +3 -0
  142. package/dist/components/charts/shared/diagnoseTypes.d.ts +10 -0
  143. package/dist/components/charts/shared/hooks.d.ts +28 -5
  144. package/dist/components/charts/shared/minMax.d.ts +3 -0
  145. package/dist/components/charts/shared/motionEncoding.d.ts +145 -0
  146. package/dist/components/charts/shared/radialGeometry.d.ts +0 -18
  147. package/dist/components/charts/shared/responsiveRules.d.ts +56 -0
  148. package/dist/components/charts/shared/smartTooltip.d.ts +27 -0
  149. package/dist/components/charts/shared/statisticalOverlays.d.ts +35 -0
  150. package/dist/components/charts/shared/streamPropsHelpers.d.ts +12 -2
  151. package/dist/components/charts/shared/temporalStrings.d.ts +7 -0
  152. package/dist/components/charts/shared/tooltipUtils.d.ts +2 -0
  153. package/dist/components/charts/shared/types.d.ts +49 -1
  154. package/dist/components/charts/shared/useChartSetup.d.ts +10 -3
  155. package/dist/components/charts/shared/useCustomChartSetup.d.ts +15 -6
  156. package/dist/components/charts/shared/useNetworkChartSetup.d.ts +6 -2
  157. package/dist/components/charts/shared/useOrdinalPieceStyle.d.ts +1 -1
  158. package/dist/components/charts/shared/useStreamingLegend.d.ts +1 -1
  159. package/dist/components/charts/shared/useSyncedPushData.d.ts +49 -0
  160. package/dist/components/charts/shared/validateChartData.d.ts +1 -1
  161. package/dist/components/charts/shared/validationMap.d.ts +0 -11
  162. package/dist/components/charts/xy/AreaChart.d.ts +1 -1
  163. package/dist/components/charts/xy/BubbleChart.d.ts +1 -1
  164. package/dist/components/charts/xy/LineChart.d.ts +1 -1
  165. package/dist/components/charts/xy/MinimapChart.d.ts +1 -1
  166. package/dist/components/charts/xy/MultiAxisLineChart.d.ts +2 -2
  167. package/dist/components/charts/xy/QuadrantChart.d.ts +1 -1
  168. package/dist/components/charts/xy/Scatterplot.d.ts +10 -1
  169. package/dist/components/charts/xy/ScatterplotMatrix.d.ts +11 -1
  170. package/dist/components/charts/xy/StackedAreaChart.d.ts +9 -4
  171. package/dist/components/controls/controlAudit.d.ts +24 -0
  172. package/dist/components/controls/controlContract.d.ts +57 -0
  173. package/dist/components/data/fromArrow.d.ts +54 -0
  174. package/dist/components/data/fromFlintChart.d.ts +74 -0
  175. package/dist/components/data/fromMermaid.d.ts +47 -0
  176. package/dist/components/data/fromObservablePlot.d.ts +69 -0
  177. package/dist/components/data/fromVegaLite.d.ts +27 -0
  178. package/dist/components/data/portability/index.d.ts +17 -0
  179. package/dist/components/data/portability/result.d.ts +52 -0
  180. package/dist/components/data/portability/spec.d.ts +160 -0
  181. package/dist/components/data/portability/vegaLite.d.ts +80 -0
  182. package/dist/components/export/chartConfig.d.ts +11 -0
  183. package/dist/components/realtime/ReorderBuffer.d.ts +54 -0
  184. package/dist/components/realtime/RunningStats.d.ts +66 -0
  185. package/dist/components/realtime/WindowAccumulator.d.ts +105 -0
  186. package/dist/components/realtime/parseWindowDuration.d.ts +12 -0
  187. package/dist/components/realtime/types.d.ts +11 -0
  188. package/dist/components/recipes/annotationLayout.d.ts +29 -0
  189. package/dist/components/recipes/axisFixedForce.d.ts +119 -0
  190. package/dist/components/recipes/boba.d.ts +23 -0
  191. package/dist/components/recipes/customTooltip.d.ts +31 -0
  192. package/dist/components/recipes/cyclical.d.ts +39 -0
  193. package/dist/components/recipes/edgeRouter.d.ts +103 -0
  194. package/dist/components/recipes/forceLayout.d.ts +48 -0
  195. package/dist/components/recipes/forceLayoutAsync.d.ts +17 -0
  196. package/dist/components/recipes/gofishBobaHandwritten.d.ts +3 -0
  197. package/dist/components/recipes/gofishDisplayListFixtures.generated.d.ts +37 -0
  198. package/dist/components/recipes/gofishIR.d.ts +215 -0
  199. package/dist/components/recipes/gofishIRExamples.d.ts +38 -0
  200. package/dist/components/recipes/intervalLanes.d.ts +87 -0
  201. package/dist/components/recipes/intervals.d.ts +80 -0
  202. package/dist/components/recipes/isometricLandmarks.d.ts +88 -0
  203. package/dist/components/recipes/isotypeGlyphs.d.ts +17 -0
  204. package/dist/components/recipes/mermaidDag.d.ts +44 -0
  205. package/dist/components/recipes/mobileAnnotationStrategy.d.ts +48 -0
  206. package/dist/components/recipes/mobileChartFamilyRecipes.d.ts +59 -0
  207. package/dist/components/recipes/networkAnalysis.d.ts +102 -0
  208. package/dist/components/recipes/networkLayouts.d.ts +83 -0
  209. package/dist/components/recipes/packedClusterMatrix.d.ts +166 -0
  210. package/dist/components/recipes/physics.d.ts +122 -0
  211. package/dist/components/recipes/physicsReference.d.ts +102 -0
  212. package/dist/components/recipes/processAggregates.d.ts +32 -0
  213. package/dist/components/recipes/processChrome.d.ts +67 -0
  214. package/dist/components/recipes/processJourney.d.ts +54 -0
  215. package/dist/components/recipes/processPhysics.d.ts +300 -0
  216. package/dist/components/recipes/processVolumeGeometry.d.ts +28 -0
  217. package/dist/components/recipes/radialCoords.d.ts +69 -0
  218. package/dist/components/recipes/random.d.ts +2 -0
  219. package/dist/components/recipes/recipeChrome.d.ts +191 -0
  220. package/dist/components/recipes/recipeGlyph.d.ts +42 -0
  221. package/dist/components/recipes/recipeLegend.d.ts +109 -0
  222. package/dist/components/recipes/recipeUtils.d.ts +157 -0
  223. package/dist/components/recipes/runs.d.ts +59 -0
  224. package/dist/components/recipes/tokenEncoding.d.ts +157 -0
  225. package/dist/components/recipes/tokenLayer.d.ts +79 -0
  226. package/dist/components/recipes/unitize.d.ts +108 -0
  227. package/dist/components/recipes/vector.d.ts +28 -0
  228. package/dist/components/recipes/waffle.d.ts +43 -0
  229. package/dist/components/semiotic-ai-core.d.ts +47 -0
  230. package/dist/components/semiotic-ai.d.ts +52 -4
  231. package/dist/components/semiotic-controls.d.ts +18 -0
  232. package/dist/components/semiotic-data.d.ts +2 -0
  233. package/dist/components/semiotic-experimental.d.ts +49 -0
  234. package/dist/components/semiotic-geo.d.ts +11 -0
  235. package/dist/components/semiotic-network.d.ts +14 -1
  236. package/dist/components/semiotic-ordinal.d.ts +10 -0
  237. package/dist/components/semiotic-physics-matter.d.ts +10 -0
  238. package/dist/components/semiotic-physics-rapier.d.ts +9 -0
  239. package/dist/components/semiotic-physics.d.ts +56 -0
  240. package/dist/components/semiotic-realtime-core.d.ts +34 -0
  241. package/dist/components/semiotic-realtime-react.d.ts +10 -0
  242. package/dist/components/semiotic-realtime.d.ts +4 -22
  243. package/dist/components/semiotic-recipes-core.d.ts +92 -0
  244. package/dist/components/semiotic-recipes-react.d.ts +7 -0
  245. package/dist/components/semiotic-recipes.d.ts +4 -26
  246. package/dist/components/semiotic-server-edge.d.ts +8 -0
  247. package/dist/components/semiotic-server-node.d.ts +5 -0
  248. package/dist/components/semiotic-server.d.ts +2 -2
  249. package/dist/components/semiotic-themes-core.d.ts +53 -0
  250. package/dist/components/semiotic-themes-react.d.ts +4 -0
  251. package/dist/components/semiotic-themes.d.ts +6 -74
  252. package/dist/components/semiotic-utils-core.d.ts +46 -0
  253. package/dist/components/semiotic-utils-react.d.ts +5 -0
  254. package/dist/components/semiotic-utils.d.ts +3 -39
  255. package/dist/components/semiotic-xy.d.ts +10 -0
  256. package/dist/components/semiotic.d.ts +39 -7
  257. package/dist/components/server/animatedGif.d.ts +36 -0
  258. package/dist/components/server/renderEvidence.d.ts +1 -1
  259. package/dist/components/server/renderToStaticSVG.d.ts +3 -35
  260. package/dist/components/server/serverChartConfigShared.d.ts +17 -0
  261. package/dist/components/server/serverChartConfigs.d.ts +19 -9
  262. package/dist/components/server/serverChartConfigsCustom.d.ts +5 -0
  263. package/dist/components/server/serverChartConfigsGeo.d.ts +9 -0
  264. package/dist/components/server/serverChartConfigsNetwork.d.ts +8 -0
  265. package/dist/components/server/serverChartConfigsOrdinal.d.ts +16 -0
  266. package/dist/components/server/serverChartConfigsPhysics.d.ts +9 -0
  267. package/dist/components/server/serverChartConfigsXY.d.ts +11 -0
  268. package/dist/components/server/staticGeo.d.ts +4 -0
  269. package/dist/components/server/staticLegend.d.ts +1 -1
  270. package/dist/components/server/staticNetwork.d.ts +8 -0
  271. package/dist/components/server/staticOrdinal.d.ts +11 -0
  272. package/dist/components/server/staticPhysics.d.ts +3 -0
  273. package/dist/components/server/staticSVGChrome.d.ts +110 -0
  274. package/dist/components/server/staticXY.d.ts +4 -0
  275. package/dist/components/store/ObservationStore.d.ts +23 -3
  276. package/dist/components/store/SelectionStore.d.ts +1 -1
  277. package/dist/components/store/ThemeStore.d.ts +1 -1
  278. package/dist/components/store/TooltipStore.d.ts +1 -1
  279. package/dist/components/store/createStore.d.ts +1 -1
  280. package/dist/components/store/designTokens.d.ts +15 -0
  281. package/dist/components/store/themeSerialization.d.ts +32 -0
  282. package/dist/components/store/useChartInterrogation.d.ts +9 -0
  283. package/dist/components/stream/AccessibleDataTable.d.ts +3 -13
  284. package/dist/components/stream/FocusRing.d.ts +4 -2
  285. package/dist/components/stream/GeoCanvasHitTester.d.ts +1 -1
  286. package/dist/components/stream/GeoPipelineStore.d.ts +28 -0
  287. package/dist/components/stream/NetworkHtmlMarksLayer.d.ts +44 -0
  288. package/dist/components/stream/NetworkPipelineStore.d.ts +62 -25
  289. package/dist/components/stream/NetworkSVGOverlay.d.ts +15 -0
  290. package/dist/components/stream/OrdinalPipelineStore.d.ts +64 -2
  291. package/dist/components/stream/PipelineStore.d.ts +100 -162
  292. package/dist/components/stream/SceneGraph.d.ts +14 -2
  293. package/dist/components/stream/StreamGeoFrame.d.ts +1 -1
  294. package/dist/components/stream/StreamNetworkFrame.d.ts +1 -1
  295. package/dist/components/stream/StreamOrdinalFrame.d.ts +1 -1
  296. package/dist/components/stream/StreamXYFrame.d.ts +2 -18
  297. package/dist/components/stream/accessibleDataRows.d.ts +33 -0
  298. package/dist/components/stream/accessorUtils.d.ts +23 -10
  299. package/dist/components/stream/brushAccessibility.d.ts +25 -0
  300. package/dist/components/stream/canvasBackground.d.ts +24 -0
  301. package/dist/components/stream/canvasSetup.d.ts +4 -1
  302. package/dist/components/stream/customLayout.d.ts +20 -0
  303. package/dist/components/stream/customLayoutDiagnostics.d.ts +11 -0
  304. package/dist/components/stream/customLayoutFailure.d.ts +33 -0
  305. package/dist/components/stream/customLayoutPalette.d.ts +19 -9
  306. package/dist/components/stream/customLayoutSelection.d.ts +52 -0
  307. package/dist/components/stream/frameThemeColors.d.ts +24 -0
  308. package/dist/components/stream/geoAnnotationAnchors.d.ts +14 -0
  309. package/dist/components/stream/geoCartogram.d.ts +17 -0
  310. package/dist/components/stream/geoCustomLayout.d.ts +59 -0
  311. package/dist/components/stream/geoDefaultTooltip.d.ts +11 -0
  312. package/dist/components/stream/geoFrameHelpers.d.ts +34 -0
  313. package/dist/components/stream/geoPipelineHelpers.d.ts +46 -0
  314. package/dist/components/stream/geoTypes.d.ts +56 -7
  315. package/dist/components/stream/glyphDef.d.ts +98 -0
  316. package/dist/components/stream/hitTarget.d.ts +170 -0
  317. package/dist/components/stream/hoverUtils.d.ts +3 -0
  318. package/dist/components/stream/keyboardNav.d.ts +4 -1
  319. package/dist/components/stream/layouts/forceLayoutPlugin.d.ts +9 -1
  320. package/dist/components/stream/layouts/forceLayoutWorkerClient.d.ts +57 -0
  321. package/dist/components/stream/networkBezier.d.ts +10 -0
  322. package/dist/components/stream/networkColorAccessors.d.ts +49 -0
  323. package/dist/components/stream/networkCustomLayout.d.ts +66 -1
  324. package/dist/components/stream/networkDefaultTooltip.d.ts +10 -0
  325. package/dist/components/stream/networkFrameInteraction.d.ts +37 -0
  326. package/dist/components/stream/networkFramePaint.d.ts +43 -0
  327. package/dist/components/stream/networkPipelineConfig.d.ts +22 -0
  328. package/dist/components/stream/networkPipelineHelpers.d.ts +12 -0
  329. package/dist/components/stream/networkRealtimeEncoding.d.ts +41 -0
  330. package/dist/components/stream/networkTypes.d.ts +104 -5
  331. package/dist/components/stream/ordinalCanvasRenderers.d.ts +7 -0
  332. package/dist/components/stream/ordinalCustomLayout.d.ts +17 -1
  333. package/dist/components/stream/ordinalDataIndex.d.ts +5 -0
  334. package/dist/components/stream/ordinalDefaultTooltip.d.ts +10 -0
  335. package/dist/components/stream/ordinalDomain.d.ts +38 -0
  336. package/dist/components/stream/ordinalPipelineUpdateResults.d.ts +12 -0
  337. package/dist/components/stream/ordinalPulse.d.ts +10 -0
  338. package/dist/components/stream/ordinalPulseResources.d.ts +3 -0
  339. package/dist/components/stream/ordinalSceneBuilders/sceneBuilderMap.d.ts +5 -0
  340. package/dist/components/stream/ordinalSceneBuilders/types.d.ts +2 -0
  341. package/dist/components/stream/ordinalSpatialIndex.d.ts +9 -0
  342. package/dist/components/stream/ordinalTypes.d.ts +56 -8
  343. package/dist/components/stream/paintNeeds.d.ts +31 -0
  344. package/dist/components/stream/physics/CapacityQueueController.d.ts +5 -0
  345. package/dist/components/stream/physics/CapacityQueueTypes.d.ts +134 -0
  346. package/dist/components/stream/physics/MatterPhysicsEngineAdapter.d.ts +42 -0
  347. package/dist/components/stream/physics/PhysicsAccessibility.d.ts +48 -0
  348. package/dist/components/stream/physics/PhysicsAnnotations.d.ts +58 -0
  349. package/dist/components/stream/physics/PhysicsBodyBudget.d.ts +26 -0
  350. package/dist/components/stream/physics/PhysicsBodySpatialIndex.d.ts +10 -0
  351. package/dist/components/stream/physics/PhysicsCanvasTheme.d.ts +26 -0
  352. package/dist/components/stream/physics/PhysicsControllers.d.ts +75 -0
  353. package/dist/components/stream/physics/PhysicsEngineAdapter.d.ts +59 -0
  354. package/dist/components/stream/physics/PhysicsEngineConformance.d.ts +23 -0
  355. package/dist/components/stream/physics/PhysicsEvidence.d.ts +25 -0
  356. package/dist/components/stream/physics/PhysicsKernel.d.ts +207 -0
  357. package/dist/components/stream/physics/PhysicsOptionalEngineAdapters.d.ts +12 -0
  358. package/dist/components/stream/physics/PhysicsPipelineStore.d.ts +84 -0
  359. package/dist/components/stream/physics/PhysicsPipelineTypes.d.ts +181 -0
  360. package/dist/components/stream/physics/PhysicsSVGOverlay.d.ts +72 -0
  361. package/dist/components/stream/physics/PhysicsSediment.d.ts +69 -0
  362. package/dist/components/stream/physics/PhysicsSettledSVG.d.ts +18 -0
  363. package/dist/components/stream/physics/PhysicsSettledScene.d.ts +21 -0
  364. package/dist/components/stream/physics/PhysicsWorkerClient.d.ts +24 -0
  365. package/dist/components/stream/physics/PhysicsWorkerProtocol.d.ts +101 -0
  366. package/dist/components/stream/physics/PhysicsWorkerRuntime.d.ts +5 -0
  367. package/dist/components/stream/physics/RapierPhysicsEngineAdapter.d.ts +18 -0
  368. package/dist/components/stream/physics/ServiceOperationsControllers.d.ts +27 -0
  369. package/dist/components/stream/physics/ServiceOperationsTypes.d.ts +89 -0
  370. package/dist/components/stream/physics/StreamPhysicsFrame.d.ts +6 -0
  371. package/dist/components/stream/physics/StreamPhysicsTypes.d.ts +302 -0
  372. package/dist/components/stream/physics/physicsBodyCanvas.d.ts +27 -0
  373. package/dist/components/stream/physics/physicsPipelineControls.d.ts +4 -0
  374. package/dist/components/stream/physics/physicsPipelineHelpers.d.ts +41 -0
  375. package/dist/components/stream/physics/physicsPipelineObservations.d.ts +14 -0
  376. package/dist/components/stream/physics/physicsPipelineUpdateResults.d.ts +9 -0
  377. package/dist/components/stream/physics/physicsRegionRuntime.d.ts +54 -0
  378. package/dist/components/stream/physics/physicsSemanticUI.d.ts +20 -0
  379. package/dist/components/stream/physics/usePhysicsFrameLifecyclePolicy.d.ts +16 -0
  380. package/dist/components/stream/pipelineBufferUtils.d.ts +26 -0
  381. package/dist/components/stream/pipelineConfig.d.ts +177 -0
  382. package/dist/components/stream/pipelineDecay.d.ts +20 -2
  383. package/dist/components/stream/pipelineDomainResolution.d.ts +62 -0
  384. package/dist/components/stream/pipelineIdentityOps.d.ts +26 -0
  385. package/dist/components/stream/pipelinePulse.d.ts +16 -2
  386. package/dist/components/stream/pipelineRibbons.d.ts +41 -0
  387. package/dist/components/stream/pipelineSpatialIndex.d.ts +12 -0
  388. package/dist/components/stream/pipelineStoreUpdateResults.d.ts +12 -0
  389. package/dist/components/stream/pipelineStyleResolvers.d.ts +46 -0
  390. package/dist/components/stream/pipelineTransitions.d.ts +2 -0
  391. package/dist/components/stream/pipelineUpdateContract.d.ts +62 -0
  392. package/dist/components/stream/pulseFrameRefresh.d.ts +22 -0
  393. package/dist/components/stream/renderers/glyphCanvasRenderer.d.ts +6 -0
  394. package/dist/components/stream/renderers/networkSymbolRenderer.d.ts +2 -0
  395. package/dist/components/stream/renderers/resolveCSSColor.d.ts +6 -0
  396. package/dist/components/stream/renderers/symbolCanvasRenderer.d.ts +2 -0
  397. package/dist/components/stream/sceneRevisionDiagnostics.d.ts +18 -0
  398. package/dist/components/stream/stalenessBands.d.ts +28 -0
  399. package/dist/components/stream/streamStoreSync.d.ts +37 -0
  400. package/dist/components/stream/symbolPath.d.ts +24 -0
  401. package/dist/components/stream/types.d.ts +206 -9
  402. package/dist/components/stream/useFrame.d.ts +21 -2
  403. package/dist/components/stream/useHydration.d.ts +6 -0
  404. package/dist/components/stream/useLegendCategoryEmission.d.ts +5 -0
  405. package/dist/components/stream/useStalenessCheck.d.ts +6 -1
  406. package/dist/components/stream/workerModuleUrl.d.ts +10 -0
  407. package/dist/components/stream/xyAnnotationAnchors.d.ts +13 -0
  408. package/dist/components/stream/xyCanvasRenderers.d.ts +7 -0
  409. package/dist/components/stream/xyCrosshair.d.ts +11 -0
  410. package/dist/components/stream/xyDateTicks.d.ts +9 -0
  411. package/dist/components/stream/xyDefaultTooltip.d.ts +10 -0
  412. package/dist/components/stream/xySceneBuilders/pointScene.d.ts +2 -2
  413. package/dist/components/stream/xySceneBuilders/types.d.ts +9 -4
  414. package/dist/controls.min.js +2 -0
  415. package/dist/controls.module.min.js +2 -0
  416. package/dist/forceLayoutWorker.js +1 -0
  417. package/dist/geo.min.js +1 -1
  418. package/dist/geo.module.min.js +1 -1
  419. package/dist/network.min.js +1 -1
  420. package/dist/network.module.min.js +1 -1
  421. package/dist/ordinal.min.js +1 -1
  422. package/dist/ordinal.module.min.js +1 -1
  423. package/dist/physics-matter.min.js +1 -0
  424. package/dist/physics-matter.module.min.js +1 -0
  425. package/dist/physics-rapier.min.js +1 -0
  426. package/dist/physics-rapier.module.min.js +1 -0
  427. package/dist/physics.min.js +2 -0
  428. package/dist/physics.module.min.js +2 -0
  429. package/dist/physicsWorker.js +1 -0
  430. package/dist/realtime.min.js +1 -1
  431. package/dist/realtime.module.min.js +1 -1
  432. package/dist/semiotic-ai-chunk-OG7BQUUW.module.min.js +2 -0
  433. package/dist/semiotic-ai-core.d.ts +47 -0
  434. package/dist/semiotic-ai-core.min.js +1 -0
  435. package/dist/semiotic-ai-core.module.min.js +1 -0
  436. package/dist/semiotic-ai-statisticalOverlays-CY4WKR4A.module.min.js +2 -0
  437. package/dist/semiotic-ai.d.ts +52 -4
  438. package/dist/semiotic-ai.min.js +1 -1
  439. package/dist/semiotic-ai.module.min.js +1 -1
  440. package/dist/semiotic-chunk-3FSVZ46U.module.min.js +2 -0
  441. package/dist/semiotic-controls.d.ts +18 -0
  442. package/dist/semiotic-data.d.ts +2 -0
  443. package/dist/semiotic-data.min.js +1 -1
  444. package/dist/semiotic-data.module.min.js +1 -1
  445. package/dist/semiotic-experimental.d.ts +49 -0
  446. package/dist/semiotic-experimental.min.js +1 -0
  447. package/dist/semiotic-experimental.module.min.js +1 -0
  448. package/dist/semiotic-geo.d.ts +11 -0
  449. package/dist/semiotic-network.d.ts +14 -1
  450. package/dist/semiotic-ordinal.d.ts +10 -0
  451. package/dist/semiotic-physics-matter.d.ts +10 -0
  452. package/dist/semiotic-physics-rapier.d.ts +9 -0
  453. package/dist/semiotic-physics.d.ts +56 -0
  454. package/dist/semiotic-realtime-core.d.ts +34 -0
  455. package/dist/semiotic-realtime-core.min.js +1 -0
  456. package/dist/semiotic-realtime-core.module.min.js +1 -0
  457. package/dist/semiotic-realtime-react.d.ts +10 -0
  458. package/dist/semiotic-realtime-react.min.js +2 -0
  459. package/dist/semiotic-realtime-react.module.min.js +2 -0
  460. package/dist/semiotic-realtime.d.ts +4 -22
  461. package/dist/semiotic-recipes-core.d.ts +92 -0
  462. package/dist/semiotic-recipes-core.min.js +1 -0
  463. package/dist/semiotic-recipes-core.module.min.js +1 -0
  464. package/dist/semiotic-recipes-react.d.ts +7 -0
  465. package/dist/semiotic-recipes-react.min.js +2 -0
  466. package/dist/semiotic-recipes-react.module.min.js +2 -0
  467. package/dist/semiotic-recipes.d.ts +4 -26
  468. package/dist/semiotic-recipes.min.js +1 -1
  469. package/dist/semiotic-recipes.module.min.js +1 -1
  470. package/dist/semiotic-server-edge.d.ts +8 -0
  471. package/dist/semiotic-server-edge.min.js +1 -0
  472. package/dist/semiotic-server-edge.module.min.js +1 -0
  473. package/dist/semiotic-server-node.d.ts +5 -0
  474. package/dist/semiotic-server-node.min.js +1 -0
  475. package/dist/semiotic-server-node.module.min.js +1 -0
  476. package/dist/semiotic-server.d.ts +2 -2
  477. package/dist/semiotic-statisticalOverlays-UOMSFKVJ.module.min.js +2 -0
  478. package/dist/semiotic-themes-core.d.ts +53 -0
  479. package/dist/semiotic-themes-core.min.js +1 -0
  480. package/dist/semiotic-themes-core.module.min.js +1 -0
  481. package/dist/semiotic-themes-react.d.ts +4 -0
  482. package/dist/semiotic-themes-react.min.js +2 -0
  483. package/dist/semiotic-themes-react.module.min.js +2 -0
  484. package/dist/semiotic-themes.d.ts +6 -74
  485. package/dist/semiotic-themes.min.js +1 -2
  486. package/dist/semiotic-themes.module.min.js +1 -2
  487. package/dist/semiotic-utils-core.d.ts +46 -0
  488. package/dist/semiotic-utils-core.min.js +1 -0
  489. package/dist/semiotic-utils-core.module.min.js +1 -0
  490. package/dist/semiotic-utils-react.d.ts +5 -0
  491. package/dist/semiotic-utils-react.min.js +2 -0
  492. package/dist/semiotic-utils-react.module.min.js +2 -0
  493. package/dist/semiotic-utils.d.ts +3 -39
  494. package/dist/semiotic-utils.min.js +1 -2
  495. package/dist/semiotic-utils.module.min.js +1 -2
  496. package/dist/semiotic-value.min.js +1 -1
  497. package/dist/semiotic-value.module.min.js +1 -1
  498. package/dist/semiotic-xy.d.ts +10 -0
  499. package/dist/semiotic.d.ts +39 -7
  500. package/dist/semiotic.min.js +1 -1
  501. package/dist/semiotic.module.min.js +1 -1
  502. package/dist/server.min.js +1 -1
  503. package/dist/server.module.min.js +1 -1
  504. package/dist/xy-chunk-IWD6IB6V.module.min.js +2 -0
  505. package/dist/xy-statisticalOverlays-7RWG6LJT.module.min.js +2 -0
  506. package/dist/xy.min.js +1 -1
  507. package/dist/xy.module.min.js +1 -1
  508. package/package.json +246 -86
  509. package/spec/README.md +152 -0
  510. package/spec/bindings/README.md +35 -0
  511. package/spec/bindings/vega-lite.mjs +112 -0
  512. package/spec/v0.1/annotation-provenance.schema.json +127 -0
  513. package/spec/v0.1/audience-profile.schema.json +67 -0
  514. package/spec/v0.1/chart-capability.schema.json +186 -0
  515. package/dist/semiotic-ai-statisticalOverlays-C2PPlmXv.js +0 -1
  516. package/dist/semiotic-statisticalOverlays-C3DsOgr_.js +0 -1
  517. package/dist/xy-statisticalOverlays-C2PPlmXv.js +0 -1
@@ -3206,11 +3206,11 @@ var require_utils = __commonJS({
3206
3206
  output.address = address.join("");
3207
3207
  return output;
3208
3208
  }
3209
- function normalizeIPv6(host) {
3210
- if (findToken(host, ":") < 2) {
3211
- return { host, isIPV6: false };
3209
+ function normalizeIPv6(host2) {
3210
+ if (findToken(host2, ":") < 2) {
3211
+ return { host: host2, isIPV6: false };
3212
3212
  }
3213
- const ipv63 = getIPV6(host);
3213
+ const ipv63 = getIPV6(host2);
3214
3214
  if (!ipv63.error) {
3215
3215
  let newHost = ipv63.address;
3216
3216
  let escapedHost = ipv63.address;
@@ -3220,7 +3220,7 @@ var require_utils = __commonJS({
3220
3220
  }
3221
3221
  return { host: newHost, isIPV6: true, escapedHost };
3222
3222
  } else {
3223
- return { host, isIPV6: false };
3223
+ return { host: host2, isIPV6: false };
3224
3224
  }
3225
3225
  }
3226
3226
  function findToken(str, token) {
@@ -3308,10 +3308,10 @@ var require_utils = __commonJS({
3308
3308
  var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
3309
3309
  var HOST_DELIM_RE = /[@/?#:]/g;
3310
3310
  var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
3311
- function reescapeHostDelimiters(host, isIP) {
3311
+ function reescapeHostDelimiters(host2, isIP) {
3312
3312
  const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
3313
3313
  re.lastIndex = 0;
3314
- return host.replace(re, (ch) => HOST_DELIMS[ch]);
3314
+ return host2.replace(re, (ch) => HOST_DELIMS[ch]);
3315
3315
  }
3316
3316
  function normalizePercentEncoding(input, decodeUnreserved = false) {
3317
3317
  if (input.indexOf("%") === -1) {
@@ -3384,16 +3384,16 @@ var require_utils = __commonJS({
3384
3384
  uriTokens.push("@");
3385
3385
  }
3386
3386
  if (component.host !== void 0) {
3387
- let host = unescape(component.host);
3388
- if (!isIPv4(host)) {
3389
- const ipV6res = normalizeIPv6(host);
3387
+ let host2 = unescape(component.host);
3388
+ if (!isIPv4(host2)) {
3389
+ const ipV6res = normalizeIPv6(host2);
3390
3390
  if (ipV6res.isIPV6 === true) {
3391
- host = `[${ipV6res.escapedHost}]`;
3391
+ host2 = `[${ipV6res.escapedHost}]`;
3392
3392
  } else {
3393
- host = reescapeHostDelimiters(host, false);
3393
+ host2 = reescapeHostDelimiters(host2, false);
3394
3394
  }
3395
3395
  }
3396
- uriTokens.push(host);
3396
+ uriTokens.push(host2);
3397
3397
  }
3398
3398
  if (typeof component.port === "number" || typeof component.port === "string") {
3399
3399
  uriTokens.push(":");
@@ -6894,7 +6894,7 @@ var require_dist = __commonJS({
6894
6894
  var require_componentMetadata = __commonJS({
6895
6895
  "ai/componentMetadata.cjs"(exports2, module2) {
6896
6896
  "use strict";
6897
- var CATEGORY_ORDER = ["xy", "ordinal", "network", "geo", "realtime", "value"];
6897
+ var CATEGORY_ORDER = ["xy", "ordinal", "network", "geo", "realtime", "physics", "value"];
6898
6898
  var COMPONENTS_BY_CATEGORY = {
6899
6899
  xy: [
6900
6900
  "LineChart",
@@ -6952,6 +6952,15 @@ var require_componentMetadata = __commonJS({
6952
6952
  "RealtimeWaterfallChart",
6953
6953
  "RealtimeHeatmap"
6954
6954
  ],
6955
+ physics: [
6956
+ "GaltonBoardChart",
6957
+ "EventDropChart",
6958
+ "PhysicsPileChart",
6959
+ "CollisionSwarmChart",
6960
+ "PhysicalFlowChart",
6961
+ "ProcessFlowChart",
6962
+ "GauntletChart"
6963
+ ],
6955
6964
  value: [
6956
6965
  "BigNumber"
6957
6966
  ]
@@ -6973,6 +6982,7 @@ var require_componentMetadata = __commonJS({
6973
6982
  return category;
6974
6983
  }
6975
6984
  function importPathForCategory(category) {
6985
+ if (category === "physics") return "semiotic/physics";
6976
6986
  return category === "geo" ? "semiotic/geo" : `semiotic/${category}`;
6977
6987
  }
6978
6988
  function metadataForComponent2(entryOrName) {
@@ -14325,13 +14335,13 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
14325
14335
  }
14326
14336
  return propValues;
14327
14337
  });
14328
- const isObject2 = isObject;
14338
+ const isObject3 = isObject;
14329
14339
  const catchall = def.catchall;
14330
14340
  let value;
14331
14341
  inst._zod.parse = (payload, ctx) => {
14332
14342
  value ?? (value = _normalized.value);
14333
14343
  const input = payload.value;
14334
- if (!isObject2(input)) {
14344
+ if (!isObject3(input)) {
14335
14345
  payload.issues.push({
14336
14346
  expected: "object",
14337
14347
  code: "invalid_type",
@@ -14429,7 +14439,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
14429
14439
  return (payload, ctx) => fn(shape, payload, ctx);
14430
14440
  };
14431
14441
  let fastpass;
14432
- const isObject2 = isObject;
14442
+ const isObject3 = isObject;
14433
14443
  const jit = !globalConfig.jitless;
14434
14444
  const allowsEval2 = allowsEval;
14435
14445
  const fastEnabled = jit && allowsEval2.value;
@@ -14438,7 +14448,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
14438
14448
  inst._zod.parse = (payload, ctx) => {
14439
14449
  value ?? (value = _normalized.value);
14440
14450
  const input = payload.value;
14441
- if (!isObject2(input)) {
14451
+ if (!isObject3(input)) {
14442
14452
  payload.issues.push({
14443
14453
  expected: "object",
14444
14454
  code: "invalid_type",
@@ -31204,8 +31214,8 @@ var newRequest = (incoming, defaultHostname) => {
31204
31214
  }
31205
31215
  return req;
31206
31216
  }
31207
- const host = (incoming instanceof import_http22.Http2ServerRequest ? incoming.authority : incoming.headers.host) || defaultHostname;
31208
- if (!host) {
31217
+ const host2 = (incoming instanceof import_http22.Http2ServerRequest ? incoming.authority : incoming.headers.host) || defaultHostname;
31218
+ if (!host2) {
31209
31219
  throw new RequestError("Missing host header");
31210
31220
  }
31211
31221
  let scheme;
@@ -31217,8 +31227,8 @@ var newRequest = (incoming, defaultHostname) => {
31217
31227
  } else {
31218
31228
  scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
31219
31229
  }
31220
- const url2 = new URL(`${scheme}://${host}${incomingUrl}`);
31221
- if (url2.hostname.length !== host.length && url2.hostname !== host.replace(/:\d+$/, "")) {
31230
+ const url2 = new URL(`${scheme}://${host2}${incomingUrl}`);
31231
+ if (url2.hostname.length !== host2.length && url2.hostname !== host2.replace(/:\d+$/, "")) {
31222
31232
  throw new RequestError("Invalid host header");
31223
31233
  }
31224
31234
  req[urlKey] = url2.href;
@@ -32356,6 +32366,584 @@ var fs = __toESM(require("fs"));
32356
32366
  var path = __toESM(require("path"));
32357
32367
  var http = __toESM(require("http"));
32358
32368
 
32369
+ // ai/mcp-server-options.ts
32370
+ var DEFAULT_HTTP_HOST = "127.0.0.1";
32371
+ function getFlagValue(args, flag) {
32372
+ const inlinePrefix = `${flag}=`;
32373
+ for (let index = 0; index < args.length; index++) {
32374
+ const argument = args[index];
32375
+ if (argument.startsWith(inlinePrefix)) return argument.slice(inlinePrefix.length);
32376
+ if (argument !== flag) continue;
32377
+ const next = args[index + 1];
32378
+ return next && !next.startsWith("--") ? next : void 0;
32379
+ }
32380
+ return void 0;
32381
+ }
32382
+ function resolveHTTPListenHost(args, env = process.env) {
32383
+ return getFlagValue(args, "--host")?.trim() || env.MCP_HOST?.trim() || DEFAULT_HTTP_HOST;
32384
+ }
32385
+
32386
+ // ai/mcp-logging.ts
32387
+ var DEFAULT_MCP_LOG_LEVEL = "info";
32388
+ var DEFAULT_MCP_LOG_RETENTION_DAYS = 30;
32389
+ var DEFAULT_MCP_LOG_MAX_EVENT_BYTES = 1024;
32390
+ var MIN_MCP_LOG_MAX_EVENT_BYTES = 256;
32391
+ var MAX_MCP_LOG_MAX_EVENT_BYTES = 4096;
32392
+ var MAX_MCP_LOG_RETENTION_DAYS = 90;
32393
+ var LEVEL_RANK = {
32394
+ error: 0,
32395
+ warn: 1,
32396
+ info: 2
32397
+ };
32398
+ var SAFE_EVENTS = /* @__PURE__ */ new Set([
32399
+ "request_completed",
32400
+ "request_failed",
32401
+ "request_rejected",
32402
+ "service_fatal",
32403
+ "service_started",
32404
+ "log_event_truncated"
32405
+ ]);
32406
+ var SAFE_REASONS = /* @__PURE__ */ new Set([
32407
+ "forbidden_host",
32408
+ "forbidden_origin",
32409
+ "invalid_json",
32410
+ "operation_limit",
32411
+ "request_body_too_large",
32412
+ "request_concurrency",
32413
+ "request_handler_error",
32414
+ "request_rate",
32415
+ "request_stream_error",
32416
+ "service_startup_failure",
32417
+ "unauthorized",
32418
+ "unsupported_accept",
32419
+ "unsupported_protocol_version"
32420
+ ]);
32421
+ var SAFE_ROUTES = /* @__PURE__ */ new Set([
32422
+ "/",
32423
+ "/mcp",
32424
+ "/health",
32425
+ "/healthz",
32426
+ "/.well-known/openai-apps-challenge"
32427
+ ]);
32428
+ function boundedPositiveInteger(value, fallback, min, max) {
32429
+ const parsed = Number(value);
32430
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) return fallback;
32431
+ return Math.min(max, Math.max(min, parsed));
32432
+ }
32433
+ function resolveMcpLoggingPolicy(env = process.env) {
32434
+ const requestedLevel = env.MCP_LOG_LEVEL?.trim().toLowerCase();
32435
+ const level = requestedLevel === "silent" || requestedLevel === "error" || requestedLevel === "warn" || requestedLevel === "info" ? requestedLevel : DEFAULT_MCP_LOG_LEVEL;
32436
+ return {
32437
+ level,
32438
+ retentionDays: boundedPositiveInteger(
32439
+ env.MCP_LOG_RETENTION_DAYS,
32440
+ DEFAULT_MCP_LOG_RETENTION_DAYS,
32441
+ 1,
32442
+ MAX_MCP_LOG_RETENTION_DAYS
32443
+ ),
32444
+ maxEventBytes: boundedPositiveInteger(
32445
+ env.MCP_LOG_MAX_EVENT_BYTES,
32446
+ DEFAULT_MCP_LOG_MAX_EVENT_BYTES,
32447
+ MIN_MCP_LOG_MAX_EVENT_BYTES,
32448
+ MAX_MCP_LOG_MAX_EVENT_BYTES
32449
+ )
32450
+ };
32451
+ }
32452
+ function safeMethod(value) {
32453
+ if (typeof value !== "string") return "other";
32454
+ const upper = value.toUpperCase();
32455
+ return upper === "GET" || upper === "POST" || upper === "OPTIONS" ? upper : "other";
32456
+ }
32457
+ function safeRoute(value) {
32458
+ return typeof value === "string" && SAFE_ROUTES.has(value) ? value : "other";
32459
+ }
32460
+ function safeCode(value, allowlist) {
32461
+ return typeof value === "string" && allowlist.has(value) ? value : "other";
32462
+ }
32463
+ function safeInteger(value, min, max) {
32464
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= min && value <= max ? value : void 0;
32465
+ }
32466
+ function sanitizeMcpLogMetadata(input = {}) {
32467
+ const metadata = {};
32468
+ if (input.method !== void 0) metadata.method = safeMethod(input.method);
32469
+ if (input.route !== void 0) metadata.route = safeRoute(input.route);
32470
+ const status = safeInteger(input.status, 100, 599);
32471
+ if (status !== void 0) metadata.status = status;
32472
+ if (input.reason !== void 0) metadata.reason = safeCode(input.reason, SAFE_REASONS);
32473
+ const durationMs = safeInteger(input.durationMs, 0, 864e5);
32474
+ if (durationMs !== void 0) metadata.durationMs = durationMs;
32475
+ const bodyBytes = safeInteger(input.bodyBytes, 0, 1073741824);
32476
+ if (bodyBytes !== void 0) metadata.bodyBytes = bodyBytes;
32477
+ if (input.profile !== void 0) {
32478
+ metadata.profile = input.profile === "public" || input.profile === "developer" ? input.profile : "other";
32479
+ }
32480
+ const retentionDays = safeInteger(input.retentionDays, 1, MAX_MCP_LOG_RETENTION_DAYS);
32481
+ if (retentionDays !== void 0) metadata.retentionDays = retentionDays;
32482
+ if (typeof input.protocolVersionPresent === "boolean") {
32483
+ metadata.protocolVersionPresent = input.protocolVersionPresent;
32484
+ }
32485
+ return metadata;
32486
+ }
32487
+ function defaultMcpLogWriter(_severity, line) {
32488
+ process.stderr.write(`${line}
32489
+ `);
32490
+ }
32491
+ function serializeBoundedRecord(record2, maxEventBytes) {
32492
+ const serialized = JSON.stringify(record2);
32493
+ if (Buffer.byteLength(serialized, "utf8") <= maxEventBytes) return serialized;
32494
+ return JSON.stringify({
32495
+ schema: "semiotic-mcp-log/v1",
32496
+ service: "semiotic-mcp",
32497
+ timestamp: record2.timestamp,
32498
+ severity: record2.severity,
32499
+ event: "log_event_truncated",
32500
+ metadata: {}
32501
+ });
32502
+ }
32503
+ function createMcpMetadataLogger(policy = resolveMcpLoggingPolicy(), writer = defaultMcpLogWriter) {
32504
+ const emit = (severity, event, metadata = {}) => {
32505
+ if (policy.level === "silent" || LEVEL_RANK[severity] > LEVEL_RANK[policy.level]) return;
32506
+ const record2 = {
32507
+ schema: "semiotic-mcp-log/v1",
32508
+ service: "semiotic-mcp",
32509
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
32510
+ severity: severity.toUpperCase(),
32511
+ event: safeCode(event, SAFE_EVENTS),
32512
+ metadata: sanitizeMcpLogMetadata(metadata)
32513
+ };
32514
+ try {
32515
+ writer(severity, serializeBoundedRecord(record2, policy.maxEventBytes));
32516
+ } catch {
32517
+ }
32518
+ };
32519
+ return {
32520
+ error: (event, metadata) => emit("error", event, metadata),
32521
+ warn: (event, metadata) => emit("warn", event, metadata),
32522
+ info: (event, metadata) => emit("info", event, metadata)
32523
+ };
32524
+ }
32525
+
32526
+ // ai/mcp-request-limits.ts
32527
+ var DEFAULT_MCP_MAX_CONCURRENT_REQUESTS = 16;
32528
+ var DEFAULT_MCP_MAX_REQUESTS_PER_WINDOW = 240;
32529
+ var DEFAULT_MCP_REQUEST_WINDOW_MS = 6e4;
32530
+ function positiveInteger(value, fallback) {
32531
+ const parsed = Number.parseInt(value || "", 10);
32532
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
32533
+ }
32534
+ function resolveMcpRequestLimits(env = process.env) {
32535
+ return {
32536
+ maxConcurrentRequests: positiveInteger(
32537
+ env.MCP_MAX_CONCURRENT_REQUESTS,
32538
+ DEFAULT_MCP_MAX_CONCURRENT_REQUESTS
32539
+ ),
32540
+ maxRequestsPerWindow: positiveInteger(
32541
+ env.MCP_MAX_REQUESTS_PER_WINDOW,
32542
+ DEFAULT_MCP_MAX_REQUESTS_PER_WINDOW
32543
+ ),
32544
+ requestWindowMs: positiveInteger(env.MCP_REQUEST_WINDOW_MS, DEFAULT_MCP_REQUEST_WINDOW_MS)
32545
+ };
32546
+ }
32547
+ function defaultWindowStart(now) {
32548
+ return now <= 0 ? 1 : now;
32549
+ }
32550
+ function createMcpRequestLimiter(limits) {
32551
+ let activeRequests = 0;
32552
+ let requestWindowStart = defaultWindowStart(Date.now());
32553
+ let requestsInWindow = 0;
32554
+ const resetWindowIfExpired = (now) => {
32555
+ const elapsed = now - requestWindowStart;
32556
+ if (elapsed >= limits.requestWindowMs || elapsed < 0) {
32557
+ requestWindowStart = now;
32558
+ requestsInWindow = 0;
32559
+ }
32560
+ };
32561
+ const snapshot = () => ({
32562
+ activeRequests,
32563
+ requestWindowStart,
32564
+ requestsInWindow
32565
+ });
32566
+ const tryAcquire = (now = Date.now()) => {
32567
+ resetWindowIfExpired(now);
32568
+ if (activeRequests >= limits.maxConcurrentRequests) {
32569
+ return {
32570
+ ok: false,
32571
+ code: "MCP_REQUEST_CONCURRENCY",
32572
+ message: `Request concurrency limit exceeded. Set MCP_MAX_CONCURRENT_REQUESTS to raise the live limit.`,
32573
+ retryAfterMs: 1e3
32574
+ };
32575
+ }
32576
+ if (requestsInWindow >= limits.maxRequestsPerWindow) {
32577
+ const retryAfterMs = Math.max(
32578
+ 0,
32579
+ requestWindowStart + limits.requestWindowMs - now
32580
+ );
32581
+ return {
32582
+ ok: false,
32583
+ code: "MCP_REQUEST_RATE",
32584
+ message: `Request rate limit exceeded. Set MCP_MAX_REQUESTS_PER_WINDOW / MCP_REQUEST_WINDOW_MS to raise the rate window/capacity.`,
32585
+ retryAfterMs
32586
+ };
32587
+ }
32588
+ activeRequests++;
32589
+ requestsInWindow++;
32590
+ let released = false;
32591
+ return {
32592
+ ok: true,
32593
+ release: () => {
32594
+ if (released) return;
32595
+ released = true;
32596
+ activeRequests = Math.max(0, activeRequests - 1);
32597
+ }
32598
+ };
32599
+ };
32600
+ return {
32601
+ snapshot,
32602
+ tryAcquire
32603
+ };
32604
+ }
32605
+
32606
+ // ai/mcp-operation-limits.ts
32607
+ var DEFAULT_MCP_MAX_ROWS = 1e4;
32608
+ var DEFAULT_MCP_MAX_CELLS = 1e5;
32609
+ var DEFAULT_MCP_MAX_NESTING_DEPTH = 64;
32610
+ function positiveInteger2(value, fallback) {
32611
+ const parsed = Number.parseInt(value || "", 10);
32612
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
32613
+ }
32614
+ function resolveMcpOperationLimits(env = process.env) {
32615
+ return {
32616
+ maxRows: positiveInteger2(env.MCP_MAX_ROWS, DEFAULT_MCP_MAX_ROWS),
32617
+ maxCells: positiveInteger2(env.MCP_MAX_CELLS, DEFAULT_MCP_MAX_CELLS),
32618
+ maxNestingDepth: positiveInteger2(
32619
+ env.MCP_MAX_NESTING_DEPTH,
32620
+ DEFAULT_MCP_MAX_NESTING_DEPTH
32621
+ )
32622
+ };
32623
+ }
32624
+ function isObject2(value) {
32625
+ return value !== null && typeof value === "object";
32626
+ }
32627
+ function inspectMcpOperationInput(input, limits) {
32628
+ let rows = 0;
32629
+ let cells = 0;
32630
+ let nestingDepth = 0;
32631
+ const seen = /* @__PURE__ */ new WeakSet();
32632
+ const stack = [{ value: input, depth: 0 }];
32633
+ while (stack.length > 0) {
32634
+ const current = stack.pop();
32635
+ const { value, depth } = current;
32636
+ if (!isObject2(value) || seen.has(value)) continue;
32637
+ seen.add(value);
32638
+ nestingDepth = Math.max(nestingDepth, depth);
32639
+ if (nestingDepth > limits.maxNestingDepth) {
32640
+ return {
32641
+ ok: false,
32642
+ limit: "nestingDepth",
32643
+ observed: nestingDepth,
32644
+ maximum: limits.maxNestingDepth
32645
+ };
32646
+ }
32647
+ if (Array.isArray(value)) {
32648
+ rows += value.length;
32649
+ if (rows > limits.maxRows) {
32650
+ return { ok: false, limit: "rows", observed: rows, maximum: limits.maxRows };
32651
+ }
32652
+ for (let index = value.length - 1; index >= 0; index--) {
32653
+ stack.push({ value: value[index], depth: depth + 1 });
32654
+ }
32655
+ continue;
32656
+ }
32657
+ const keys = Object.keys(value);
32658
+ cells += keys.length;
32659
+ if (cells > limits.maxCells) {
32660
+ return { ok: false, limit: "cells", observed: cells, maximum: limits.maxCells };
32661
+ }
32662
+ for (let index = keys.length - 1; index >= 0; index--) {
32663
+ stack.push({ value: value[keys[index]], depth: depth + 1 });
32664
+ }
32665
+ }
32666
+ return { ok: true, rows, cells, nestingDepth };
32667
+ }
32668
+ function formatMcpOperationLimitError(result) {
32669
+ if (result.ok) return "";
32670
+ switch (result.limit) {
32671
+ case "rows":
32672
+ return `Tool arguments exceed the collection-entry limit (${result.observed} > ${result.maximum}; set MCP_MAX_ROWS to adjust).`;
32673
+ case "cells":
32674
+ return `Tool arguments exceed the object-field limit (${result.observed} > ${result.maximum}; set MCP_MAX_CELLS to adjust).`;
32675
+ case "nestingDepth":
32676
+ return `Tool arguments exceed the nesting-depth limit (${result.observed} > ${result.maximum}; set MCP_MAX_NESTING_DEPTH to adjust).`;
32677
+ }
32678
+ }
32679
+
32680
+ // ai/mcp-render-output-limits.ts
32681
+ var DEFAULT_MCP_MAX_RENDER_OUTPUT_BYTES = 2 * 1024 * 1024;
32682
+ var DEFAULT_MCP_MAX_WIDGET_OUTPUT_BYTES = 2 * 1024 * 1024;
32683
+ var DEFAULT_MCP_MAX_WIDGET_METADATA_BYTES = 64 * 1024;
32684
+ var DEFAULT_MCP_MAX_WIDGET_ROWS = 50;
32685
+ var DEFAULT_MCP_MAX_WIDGET_COLUMNS = 16;
32686
+ var DEFAULT_MCP_MAX_WIDGET_VALUE_BYTES = 256;
32687
+ var WIDGET_COLUMN_LABEL_BYTES = 96;
32688
+ var WIDGET_EVIDENCE_ARRAY_ITEMS = 32;
32689
+ var WIDGET_EVIDENCE_OBJECT_KEYS = 16;
32690
+ function positiveInteger3(value, fallback) {
32691
+ const parsed = Number.parseInt(value || "", 10);
32692
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
32693
+ }
32694
+ function resolveMcpRenderOutputLimits(env = process.env) {
32695
+ return {
32696
+ maxRenderOutputBytes: positiveInteger3(
32697
+ env.MCP_MAX_RENDER_OUTPUT_BYTES,
32698
+ DEFAULT_MCP_MAX_RENDER_OUTPUT_BYTES
32699
+ ),
32700
+ maxWidgetOutputBytes: positiveInteger3(
32701
+ env.MCP_MAX_WIDGET_OUTPUT_BYTES,
32702
+ DEFAULT_MCP_MAX_WIDGET_OUTPUT_BYTES
32703
+ ),
32704
+ maxWidgetMetadataBytes: positiveInteger3(
32705
+ env.MCP_MAX_WIDGET_METADATA_BYTES,
32706
+ DEFAULT_MCP_MAX_WIDGET_METADATA_BYTES
32707
+ ),
32708
+ maxWidgetRows: positiveInteger3(env.MCP_MAX_WIDGET_ROWS, DEFAULT_MCP_MAX_WIDGET_ROWS),
32709
+ maxWidgetColumns: positiveInteger3(env.MCP_MAX_WIDGET_COLUMNS, DEFAULT_MCP_MAX_WIDGET_COLUMNS),
32710
+ maxWidgetValueBytes: positiveInteger3(
32711
+ env.MCP_MAX_WIDGET_VALUE_BYTES,
32712
+ DEFAULT_MCP_MAX_WIDGET_VALUE_BYTES
32713
+ )
32714
+ };
32715
+ }
32716
+ function utf8ByteLength(value) {
32717
+ return Buffer.byteLength(value, "utf8");
32718
+ }
32719
+ function serializedMcpOutputBytes(value) {
32720
+ const serialized = JSON.stringify(value);
32721
+ return utf8ByteLength(serialized === void 0 ? "" : serialized);
32722
+ }
32723
+ function truncateUtf8(value, maximumBytes) {
32724
+ if (maximumBytes <= 0 || !value) return "";
32725
+ if (utf8ByteLength(value) <= maximumBytes) return value;
32726
+ const marker = "\u2026";
32727
+ const markerBytes = utf8ByteLength(marker);
32728
+ if (maximumBytes < markerBytes) return "";
32729
+ const targetBytes = maximumBytes - markerBytes;
32730
+ let output = "";
32731
+ let usedBytes = 0;
32732
+ for (const character of value) {
32733
+ const characterBytes = utf8ByteLength(character);
32734
+ if (usedBytes + characterBytes > targetBytes) break;
32735
+ output += character;
32736
+ usedBytes += characterBytes;
32737
+ }
32738
+ return `${output}${marker}`;
32739
+ }
32740
+ function inspectMcpOutputLimit(value, maximum) {
32741
+ const observed = serializedMcpOutputBytes(value);
32742
+ return observed <= maximum ? { ok: true, observed, maximum } : { ok: false, observed, maximum };
32743
+ }
32744
+ function formatMcpOutputLimitError(args) {
32745
+ return `${args.label} output exceeds the configured response limit (${args.limit.observed} > ${args.limit.maximum} bytes; set ${args.setting} to adjust). Reduce chart data or request a smaller render.`;
32746
+ }
32747
+ function isRecord(value) {
32748
+ return value !== null && typeof value === "object" && !Array.isArray(value);
32749
+ }
32750
+ function isSensitiveField(name) {
32751
+ return /password|passwd|secret|token|api[-_]?key|authorization|cookie|credential|private[-_]?key/i.test(name);
32752
+ }
32753
+ function previewColumnLabel(key, used) {
32754
+ const base = truncateUtf8(key || "value", WIDGET_COLUMN_LABEL_BYTES) || "value";
32755
+ let label = base;
32756
+ let suffix = 2;
32757
+ while (used.has(label)) {
32758
+ label = `${base} (${suffix})`;
32759
+ suffix += 1;
32760
+ }
32761
+ used.add(label);
32762
+ return label;
32763
+ }
32764
+ function selectPreviewCollection(props) {
32765
+ for (const collection of ["data", "nodes", "edges", "links"]) {
32766
+ if (Array.isArray(props[collection])) {
32767
+ return { collection, rows: props[collection] };
32768
+ }
32769
+ }
32770
+ return { collection: null, rows: [] };
32771
+ }
32772
+ function collectPreviewColumns(rows, limits) {
32773
+ const keys = [];
32774
+ const seen = /* @__PURE__ */ new Set();
32775
+ let truncated = false;
32776
+ for (const row of rows) {
32777
+ if (isRecord(row)) {
32778
+ for (const key of Object.keys(row)) {
32779
+ if (seen.has(key)) continue;
32780
+ if (keys.length >= limits.maxWidgetColumns) {
32781
+ truncated = true;
32782
+ break;
32783
+ }
32784
+ seen.add(key);
32785
+ keys.push(key);
32786
+ }
32787
+ } else if (!seen.has("value")) {
32788
+ if (keys.length >= limits.maxWidgetColumns) {
32789
+ truncated = true;
32790
+ } else {
32791
+ seen.add("value");
32792
+ keys.push("value");
32793
+ }
32794
+ }
32795
+ if (keys.length >= limits.maxWidgetColumns && truncated) break;
32796
+ }
32797
+ const usedLabels = /* @__PURE__ */ new Set();
32798
+ return {
32799
+ columns: keys.map((key) => ({ key, label: previewColumnLabel(key, usedLabels) })),
32800
+ truncated
32801
+ };
32802
+ }
32803
+ function previewCell(value, fieldName, limits) {
32804
+ if (isSensitiveField(fieldName)) {
32805
+ return { value: "[redacted]", redacted: true, truncated: false };
32806
+ }
32807
+ if (value === null || value === void 0) {
32808
+ return { value: null, redacted: false, truncated: false };
32809
+ }
32810
+ if (typeof value === "string") {
32811
+ const bounded = truncateUtf8(value, limits.maxWidgetValueBytes);
32812
+ return { value: bounded, redacted: false, truncated: bounded !== value };
32813
+ }
32814
+ if (typeof value === "number") {
32815
+ return Number.isFinite(value) ? { value, redacted: false, truncated: false } : { value: String(value), redacted: false, truncated: false };
32816
+ }
32817
+ if (typeof value === "boolean") return { value, redacted: false, truncated: false };
32818
+ if (Array.isArray(value)) {
32819
+ return { value: `[array: ${value.length} items]`, redacted: false, truncated: true };
32820
+ }
32821
+ if (typeof value === "object") {
32822
+ return { value: "[object]", redacted: false, truncated: true };
32823
+ }
32824
+ return {
32825
+ value: truncateUtf8(String(value), limits.maxWidgetValueBytes),
32826
+ redacted: false,
32827
+ truncated: true
32828
+ };
32829
+ }
32830
+ function createWidgetDataPreview(props, limits = resolveMcpRenderOutputLimits()) {
32831
+ const selected = selectPreviewCollection(props);
32832
+ const inputRows = selected.rows.slice(0, limits.maxWidgetRows);
32833
+ const columnSelection = collectPreviewColumns(inputRows, limits);
32834
+ const rows = [];
32835
+ let redactedFields = 0;
32836
+ let truncatedValues = 0;
32837
+ let metadataBoundReached = false;
32838
+ for (const input of inputRows) {
32839
+ const row = {};
32840
+ let rowRedactedFields = 0;
32841
+ let rowTruncatedValues = 0;
32842
+ for (const column of columnSelection.columns) {
32843
+ const hasValue = isRecord(input) ? Object.prototype.hasOwnProperty.call(input, column.key) : column.key === "value";
32844
+ const value = isRecord(input) ? input[column.key] : input;
32845
+ if (!hasValue) {
32846
+ row[column.label] = null;
32847
+ continue;
32848
+ }
32849
+ const cell = previewCell(value, column.key, limits);
32850
+ row[column.label] = cell.value;
32851
+ if (cell.redacted) rowRedactedFields += 1;
32852
+ if (cell.truncated) rowTruncatedValues += 1;
32853
+ }
32854
+ const candidate = {
32855
+ collection: selected.collection,
32856
+ totalRows: selected.rows.length,
32857
+ returnedRows: rows.length + 1,
32858
+ returnedColumns: columnSelection.columns.length,
32859
+ rows: [...rows, row],
32860
+ redactedFields: redactedFields + rowRedactedFields,
32861
+ truncatedValues: truncatedValues + rowTruncatedValues,
32862
+ truncated: true
32863
+ };
32864
+ if (serializedMcpOutputBytes(candidate) > limits.maxWidgetMetadataBytes) {
32865
+ metadataBoundReached = true;
32866
+ break;
32867
+ }
32868
+ rows.push(row);
32869
+ redactedFields += rowRedactedFields;
32870
+ truncatedValues += rowTruncatedValues;
32871
+ }
32872
+ const truncated = metadataBoundReached || columnSelection.truncated || selected.rows.length > rows.length || truncatedValues > 0;
32873
+ return {
32874
+ collection: selected.collection,
32875
+ totalRows: selected.rows.length,
32876
+ returnedRows: rows.length,
32877
+ returnedColumns: columnSelection.columns.length,
32878
+ rows,
32879
+ redactedFields,
32880
+ truncatedValues,
32881
+ truncated
32882
+ };
32883
+ }
32884
+ function finiteNumber(value) {
32885
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
32886
+ }
32887
+ function boundedEvidenceStrings(value, limits) {
32888
+ if (!Array.isArray(value)) return void 0;
32889
+ const values = value.slice(0, WIDGET_EVIDENCE_ARRAY_ITEMS).map((item) => truncateUtf8(String(item), limits.maxWidgetValueBytes));
32890
+ return { values, truncated: value.length > values.length };
32891
+ }
32892
+ function createWidgetEvidencePreview(evidence, limits = resolveMcpRenderOutputLimits()) {
32893
+ if (!evidence) return null;
32894
+ const preview = {};
32895
+ for (const key of ["component", "frameType", "status", "ariaLabel"]) {
32896
+ if (typeof evidence[key] === "string") {
32897
+ preview[key] = truncateUtf8(evidence[key], limits.maxWidgetValueBytes);
32898
+ }
32899
+ }
32900
+ if (typeof evidence.empty === "boolean") preview.empty = evidence.empty;
32901
+ for (const key of [
32902
+ "markCount",
32903
+ "width",
32904
+ "height",
32905
+ "nodeCount",
32906
+ "edgeCount",
32907
+ "legendItems",
32908
+ "annotationCount"
32909
+ ]) {
32910
+ const value = finiteNumber(evidence[key]);
32911
+ if (value !== void 0) preview[key] = value;
32912
+ }
32913
+ for (const key of ["xDomain", "yDomain"]) {
32914
+ const domain2 = evidence[key];
32915
+ if (Array.isArray(domain2) && domain2.length === 2) {
32916
+ const values = domain2.map(finiteNumber);
32917
+ if (values[0] !== void 0 && values[1] !== void 0) preview[key] = values;
32918
+ }
32919
+ }
32920
+ const categories = boundedEvidenceStrings(evidence.categories, limits);
32921
+ if (categories) {
32922
+ preview.categories = categories.values;
32923
+ if (categories.truncated) preview.categoriesTruncated = true;
32924
+ }
32925
+ const warnings = boundedEvidenceStrings(evidence.warnings, limits);
32926
+ if (warnings) {
32927
+ preview.warnings = warnings.values;
32928
+ if (warnings.truncated) preview.warningsTruncated = true;
32929
+ }
32930
+ if (isRecord(evidence.markCountByType)) {
32931
+ const markCountByType = {};
32932
+ const entries = Object.entries(evidence.markCountByType).sort(([left], [right]) => left.localeCompare(right)).slice(0, WIDGET_EVIDENCE_OBJECT_KEYS);
32933
+ for (const [key, value] of entries) {
32934
+ const count = finiteNumber(value);
32935
+ if (count !== void 0) {
32936
+ markCountByType[truncateUtf8(key, WIDGET_COLUMN_LABEL_BYTES)] = count;
32937
+ }
32938
+ }
32939
+ preview.markCountByType = markCountByType;
32940
+ if (Object.keys(evidence.markCountByType).length > entries.length) {
32941
+ preview.markCountByTypeTruncated = true;
32942
+ }
32943
+ }
32944
+ return preview;
32945
+ }
32946
+
32359
32947
  // ai/renderHOCToSVG.tsx
32360
32948
  var React = __toESM(require("react"));
32361
32949
  var ReactDOMServer = __toESM(require("react-dom/server"));
@@ -32404,7 +32992,14 @@ var COMPONENT_REGISTRY = {
32404
32992
  ChoroplethMap: { component: import_geo.ChoroplethMap, category: "geo" },
32405
32993
  ProportionalSymbolMap: { component: import_geo.ProportionalSymbolMap, category: "geo" },
32406
32994
  FlowMap: { component: import_geo.FlowMap, category: "geo" },
32407
- DistanceCartogram: { component: import_geo.DistanceCartogram, category: "geo" }
32995
+ DistanceCartogram: { component: import_geo.DistanceCartogram, category: "geo" },
32996
+ GaltonBoardChart: { component: import_ai.GaltonBoardChart, category: "physics" },
32997
+ EventDropChart: { component: import_ai.EventDropChart, category: "physics" },
32998
+ PhysicsPileChart: { component: import_ai.PhysicsPileChart, category: "physics" },
32999
+ CollisionSwarmChart: { component: import_ai.CollisionSwarmChart, category: "physics" },
33000
+ PhysicalFlowChart: { component: import_ai.PhysicalFlowChart, category: "physics" },
33001
+ ProcessFlowChart: { component: import_ai.ProcessFlowChart, category: "physics" },
33002
+ GauntletChart: { component: import_ai.GauntletChart, category: "physics" }
32408
33003
  };
32409
33004
 
32410
33005
  // ai/renderHOCToSVG.tsx
@@ -32473,6 +33068,79 @@ var componentNames = Object.keys(COMPONENT_REGISTRY).sort();
32473
33068
  var REPO = "nteract/semiotic";
32474
33069
  var SEMIOTIC_CHART_WIDGET_URI = "ui://semiotic/chart-widget.html";
32475
33070
  var MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
33071
+ var DEFAULT_MCP_SUPPORTED_PROTOCOL_VERSION = "2024-11-05";
33072
+ var DEFAULT_MCP_MAX_RENDER_WORK_MS = 2500;
33073
+ var DEFAULT_MCP_MAX_PNG_CONVERSION_MS = 4e3;
33074
+ var DEFAULT_MCP_MAX_INTERACTIVE_SVG_SANITIZE_MS = 3e3;
33075
+ var mcpLoggingPolicy = resolveMcpLoggingPolicy();
33076
+ var mcpLogger = createMcpMetadataLogger(mcpLoggingPolicy);
33077
+ function writeJsonRpcError(res, status, code, message) {
33078
+ res.writeHead(status, { "Content-Type": "application/json" });
33079
+ res.end(JSON.stringify({
33080
+ jsonrpc: "2.0",
33081
+ error: { code, message },
33082
+ id: null
33083
+ }));
33084
+ }
33085
+ function isAuthorizedRequest(req, token, scheme) {
33086
+ if (!token) return true;
33087
+ const authorization = req.headers.authorization;
33088
+ if (typeof authorization !== "string") return false;
33089
+ const [providedScheme, providedToken] = authorization.split(/\s+/, 2);
33090
+ return providedScheme?.toLowerCase() === scheme.toLowerCase() && Boolean(providedToken) && providedToken === token;
33091
+ }
33092
+ function hasSupportedAccept(acceptHeader) {
33093
+ if (!acceptHeader) return true;
33094
+ const lower = acceptHeader.toLowerCase();
33095
+ return lower.includes("*/*") || lower.includes("application/json") || lower.includes("text/event-stream");
33096
+ }
33097
+ function isSupportedProtocolVersion(protocolVersion, supported) {
33098
+ if (!protocolVersion || supported.length === 0) return true;
33099
+ return supported.includes(protocolVersion);
33100
+ }
33101
+ function parsePositiveInteger(value, fallback) {
33102
+ const parsed = Number.parseInt(value || "", 10);
33103
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
33104
+ }
33105
+ function resolveMcpRenderExecutionLimits(env = process.env) {
33106
+ return {
33107
+ maxRenderWorkMs: parsePositiveInteger(env.MCP_MAX_RENDER_WORK_MS, DEFAULT_MCP_MAX_RENDER_WORK_MS),
33108
+ maxPngConversionMs: parsePositiveInteger(env.MCP_MAX_PNG_CONVERSION_MS, DEFAULT_MCP_MAX_PNG_CONVERSION_MS),
33109
+ maxInteractiveSanitizeMs: parsePositiveInteger(env.MCP_MAX_INTERACTIVE_SVG_SANITIZE_MS, DEFAULT_MCP_MAX_INTERACTIVE_SVG_SANITIZE_MS)
33110
+ };
33111
+ }
33112
+ function makeRenderExecutionError(code, label, limitMs, observedMs) {
33113
+ const text = code === "MCP_RENDER_TIMEOUT" ? `${label} exceeded ${limitMs} ms timeout budget (${observedMs ?? 0} ms). Set ${label === "PNG conversion" ? "MCP_MAX_PNG_CONVERSION_MS" : label.includes("sanitize") ? "MCP_MAX_INTERACTIVE_SVG_SANITIZE_MS" : "MCP_MAX_RENDER_WORK_MS"} to adjust.` : `${label} was canceled before completion.`;
33114
+ const error48 = new Error(text);
33115
+ error48.code = code;
33116
+ return error48;
33117
+ }
33118
+ function throwIfRequestCanceled(signal, label = "render work") {
33119
+ if (signal?.aborted) {
33120
+ throw makeRenderExecutionError("MCP_RENDER_CANCELLED", label, 0);
33121
+ }
33122
+ }
33123
+ async function runRenderStep(label, limitMs, signal, work) {
33124
+ throwIfRequestCanceled(signal, label);
33125
+ const started = Date.now();
33126
+ const result = await Promise.resolve(work());
33127
+ if (signal?.aborted) throw makeRenderExecutionError("MCP_RENDER_CANCELLED", label, 0);
33128
+ const elapsed = Date.now() - started;
33129
+ if (limitMs > 0 && elapsed > limitMs) {
33130
+ throw makeRenderExecutionError("MCP_RENDER_TIMEOUT", label, limitMs, elapsed);
33131
+ }
33132
+ return result;
33133
+ }
33134
+ function isRenderExecutionError(error48) {
33135
+ return !!error48 && typeof error48 === "object" && (error48.code === "MCP_RENDER_TIMEOUT" || error48.code === "MCP_RENDER_CANCELLED");
33136
+ }
33137
+ function renderExecutionErrorResult(message, code) {
33138
+ return {
33139
+ content: [{ type: "text", text: message }],
33140
+ isError: true,
33141
+ structuredContent: { code }
33142
+ };
33143
+ }
32476
33144
  function aiFilePath(fileName) {
32477
33145
  return path.resolve(__dirname, "..", fileName);
32478
33146
  }
@@ -32526,11 +33194,203 @@ function promptMessage(text) {
32526
33194
  }]
32527
33195
  };
32528
33196
  }
32529
- function stripUnsafeSvg(svg) {
32530
- return svg.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\s(href|xlink:href)\s*=\s*(["'])\s*javascript:[^"']*\2/gi, "");
33197
+ var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
33198
+ var SAFE_SVG_ELEMENTS = /* @__PURE__ */ new Set([
33199
+ "svg",
33200
+ "g",
33201
+ "path",
33202
+ "line",
33203
+ "polyline",
33204
+ "polygon",
33205
+ "rect",
33206
+ "circle",
33207
+ "ellipse",
33208
+ "text",
33209
+ "tspan",
33210
+ "title",
33211
+ "desc",
33212
+ "defs",
33213
+ "style",
33214
+ "linearGradient",
33215
+ "radialGradient",
33216
+ "stop",
33217
+ "clipPath",
33218
+ "mask",
33219
+ "pattern",
33220
+ "filter",
33221
+ "marker",
33222
+ "use",
33223
+ "symbol"
33224
+ ]);
33225
+ var SAFE_SVG_ATTRIBUTES = /* @__PURE__ */ new Set([
33226
+ "alignment-baseline",
33227
+ "alignmentadjust",
33228
+ "aria-hidden",
33229
+ "aria-label",
33230
+ "aria-labelledby",
33231
+ "aria-describedby",
33232
+ "clip-path",
33233
+ "cx",
33234
+ "cy",
33235
+ "d",
33236
+ "dx",
33237
+ "dy",
33238
+ "dominant-baseline",
33239
+ "fill",
33240
+ "fill-opacity",
33241
+ "fill-rule",
33242
+ "font-family",
33243
+ "font-size",
33244
+ "font-style",
33245
+ "font-weight",
33246
+ "gradientunits",
33247
+ "gradienttransform",
33248
+ "height",
33249
+ "id",
33250
+ "marker-height",
33251
+ "marker-units",
33252
+ "marker-width",
33253
+ "marker-end",
33254
+ "marker-mid",
33255
+ "marker-start",
33256
+ "offset",
33257
+ "opacity",
33258
+ "orientation",
33259
+ "pattern-content-units",
33260
+ "pattern-transform",
33261
+ "pattern-units",
33262
+ "preserveaspectratio",
33263
+ "r",
33264
+ "rx",
33265
+ "ry",
33266
+ "role",
33267
+ "shape-rendering",
33268
+ "spreadmethod",
33269
+ "startoffset",
33270
+ "stroke",
33271
+ "stroke-linecap",
33272
+ "stroke-linejoin",
33273
+ "stroke-opacity",
33274
+ "stroke-width",
33275
+ "style",
33276
+ "text-anchor",
33277
+ "transform",
33278
+ "viewbox",
33279
+ "width",
33280
+ "x",
33281
+ "x1",
33282
+ "x2",
33283
+ "xmlns",
33284
+ "xmlns:xlink",
33285
+ "y",
33286
+ "y1",
33287
+ "y2",
33288
+ "xml:space",
33289
+ "class"
33290
+ ]);
33291
+ var SAFE_URL_ATTR_PREFIXES = [
33292
+ "http://",
33293
+ "https://",
33294
+ "#",
33295
+ "/",
33296
+ "./",
33297
+ "../",
33298
+ "mailto:",
33299
+ "tel:",
33300
+ "data:image/"
33301
+ ];
33302
+ function isSafeUrlValue(value) {
33303
+ const trimmed = value.trim().toLowerCase();
33304
+ if (!trimmed) return true;
33305
+ if (/^(javascript|vbscript|file):/i.test(trimmed)) return false;
33306
+ return SAFE_URL_ATTR_PREFIXES.some((prefix) => trimmed.startsWith(prefix));
33307
+ }
33308
+ function sanitizeStyleValue(value) {
33309
+ return value.replace(/\/\*[\s\S]*?\*\//g, "").replace(/@import[^;]*;/gi, "").replace(/expression\s*\([^)]*\)/gi, "").replace(/url\(\s*["']?\s*([^)"']+)\s*["']?\s*\)/gi, (_match, rawUrl) => {
33310
+ const safeUrl = String(rawUrl || "").trim().toLowerCase();
33311
+ if (safeUrl && !isSafeUrlValue(safeUrl)) return "url()";
33312
+ return `url(${rawUrl})`;
33313
+ });
33314
+ }
33315
+ function isAllowedSvgAttribute(name) {
33316
+ const lower = name.toLowerCase();
33317
+ if (lower.startsWith("on")) return false;
33318
+ if (lower.startsWith("data-")) return true;
33319
+ if (lower.startsWith("aria-")) return true;
33320
+ if (lower.startsWith("xml:")) return true;
33321
+ if (lower === "xmlns" || lower.startsWith("xmlns:")) return true;
33322
+ if (SAFE_SVG_ATTRIBUTES.has(lower)) return true;
33323
+ return /^[a-z][a-z0-9-_:.]*$/.test(lower);
33324
+ }
33325
+ function sanitizeSvgAttribute(name, value) {
33326
+ const lower = name.toLowerCase();
33327
+ if (!isAllowedSvgAttribute(name)) return null;
33328
+ if ((lower === "href" || lower === "xlink:href" || lower === "src") && !isSafeUrlValue(value)) {
33329
+ return null;
33330
+ }
33331
+ if (lower === "style") return sanitizeStyleValue(value);
33332
+ return value;
33333
+ }
33334
+ function sanitizeSvgNode(node, doc) {
33335
+ if (node.nodeType === 3 || node.nodeType === 4) {
33336
+ const text = node.textContent ?? "";
33337
+ return text ? doc.createTextNode(text) : null;
33338
+ }
33339
+ if (node.nodeType !== 1) return null;
33340
+ const source = node;
33341
+ const tag = source.tagName;
33342
+ if (!SAFE_SVG_ELEMENTS.has(tag)) return null;
33343
+ const safe = doc.createElementNS(SVG_NAMESPACE, tag);
33344
+ const isStyleTag = tag === "style";
33345
+ if (!isStyleTag) {
33346
+ for (const attribute of Array.from(source.attributes)) {
33347
+ const safeValue = sanitizeSvgAttribute(attribute.name, attribute.value);
33348
+ if (safeValue == null) continue;
33349
+ safe.setAttribute(attribute.name, safeValue);
33350
+ }
33351
+ } else {
33352
+ const styleText = sanitizeStyleValue(source.textContent ?? "");
33353
+ if (styleText) {
33354
+ safe.appendChild(doc.createTextNode(styleText));
33355
+ }
33356
+ return safe;
33357
+ }
33358
+ for (const child of Array.from(source.childNodes)) {
33359
+ const sanitized = sanitizeSvgNode(child, doc);
33360
+ if (sanitized) safe.appendChild(sanitized);
33361
+ }
33362
+ return safe;
33363
+ }
33364
+ var jsdomModulePromise = null;
33365
+ function loadJsdomModule() {
33366
+ if (!jsdomModulePromise) jsdomModulePromise = import("jsdom");
33367
+ return jsdomModulePromise;
33368
+ }
33369
+ void loadJsdomModule().catch(() => {
33370
+ });
33371
+ async function sanitizeSvgForWidget(svg) {
33372
+ const trimmed = svg.trim();
33373
+ if (!trimmed) return "";
33374
+ try {
33375
+ const { JSDOM } = await loadJsdomModule();
33376
+ const parsed = new JSDOM(trimmed, { contentType: "image/svg+xml" });
33377
+ const parsedDocument = parsed.window.document;
33378
+ const sourceRoot = parsedDocument.documentElement;
33379
+ if (!sourceRoot || sourceRoot.tagName.toLowerCase() !== "svg") return "";
33380
+ if (parsedDocument.getElementsByTagName("parsererror")[0]) return "";
33381
+ const cleanDocument = parsedDocument.implementation.createDocument(SVG_NAMESPACE, null, null);
33382
+ const safeRoot = sanitizeSvgNode(sourceRoot, cleanDocument);
33383
+ if (!safeRoot) return "";
33384
+ cleanDocument.appendChild(safeRoot);
33385
+ return new parsed.window.XMLSerializer().serializeToString(safeRoot);
33386
+ } catch {
33387
+ return "";
33388
+ }
32531
33389
  }
32532
33390
  function parseRenderEvidence(result) {
32533
- const evidenceText = result.content.find((block) => block.text.startsWith("Render evidence:\n"))?.text;
33391
+ const evidenceText = result.content.find(
33392
+ (block) => block.type === "text" && block.text.startsWith("Render evidence:\n")
33393
+ )?.text;
32534
33394
  if (!evidenceText) return null;
32535
33395
  try {
32536
33396
  return JSON.parse(evidenceText.replace(/^Render evidence:\n/, ""));
@@ -32539,7 +33399,8 @@ function parseRenderEvidence(result) {
32539
33399
  }
32540
33400
  }
32541
33401
  function chartTitleFromProps(component, props) {
32542
- return typeof props.title === "string" && props.title.trim() ? props.title.trim() : component;
33402
+ const title = typeof props.title === "string" && props.title.trim() ? props.title.trim() : component;
33403
+ return truncateUtf8(title, resolveMcpRenderOutputLimits().maxWidgetValueBytes);
32543
33404
  }
32544
33405
  function chartDatumCount(props) {
32545
33406
  if (Array.isArray(props.data)) return props.data.length;
@@ -32704,22 +33565,26 @@ function renderSemioticChartWidgetHTML() {
32704
33565
  return { output, meta };
32705
33566
  }
32706
33567
 
32707
- function sampleRows(meta) {
32708
- const props = meta?.props || {};
32709
- if (Array.isArray(props.data)) return props.data.slice(0, 50);
32710
- if (Array.isArray(props.nodes)) return props.nodes.slice(0, 50);
32711
- if (Array.isArray(props.edges)) return props.edges.slice(0, 50);
32712
- if (Array.isArray(props.links)) return props.links.slice(0, 50);
32713
- return [];
33568
+ function dataPreview(meta) {
33569
+ const preview = meta?.dataPreview;
33570
+ return preview && Array.isArray(preview.rows) ? preview : null;
32714
33571
  }
32715
33572
 
32716
- function renderTable(rows) {
33573
+ function renderTable(preview) {
33574
+ const rows = preview?.rows || [];
32717
33575
  if (!rows.length) return '<pre>No row data was provided in the widget metadata.</pre>';
32718
33576
  const columns = Array.from(rows.reduce((set, row) => {
32719
33577
  Object.keys(row || {}).forEach((key) => set.add(key));
32720
33578
  return set;
32721
33579
  }, new Set()));
32722
- return '<table><thead><tr>' + columns.map((col) => '<th>' + html(col) + '</th>').join('') +
33580
+ const totalRows = Number.isFinite(preview?.totalRows) ? preview.totalRows : rows.length;
33581
+ const collection = preview?.collection || 'data';
33582
+ const notes = [
33583
+ 'Showing ' + rows.length + ' of ' + totalRows + ' ' + html(collection) + ' rows.',
33584
+ preview?.truncated ? 'Preview values or rows were truncated.' : '',
33585
+ preview?.redactedFields ? 'Sensitive fields were redacted.' : ''
33586
+ ].filter(Boolean).join(' ');
33587
+ return '<div class="summary">' + notes + '</div><table><thead><tr>' + columns.map((col) => '<th>' + html(col) + '</th>').join('') +
32723
33588
  '</tr></thead><tbody>' + rows.map((row) => '<tr>' + columns.map((col) => '<td>' + html(row?.[col]) + '</td>').join('') + '</tr>').join('') + '</tbody></table>';
32724
33589
  }
32725
33590
 
@@ -32734,8 +33599,8 @@ function renderSemioticChartWidgetHTML() {
32734
33599
  } else {
32735
33600
  chartEl.innerHTML = '<div class="empty">No SVG payload received. The model-visible chart summary is still available above.</div>';
32736
33601
  }
32737
- const rows = sampleRows(hidden);
32738
- dataDrawer.innerHTML = renderTable(rows);
33602
+ const preview = dataPreview(hidden);
33603
+ dataDrawer.innerHTML = renderTable(preview);
32739
33604
  evidenceText.textContent = JSON.stringify(payload.evidence || hidden.evidence || {}, null, 2);
32740
33605
  }
32741
33606
 
@@ -32799,6 +33664,46 @@ function renderSemioticChartWidgetHTML() {
32799
33664
  </body>
32800
33665
  </html>`.trim();
32801
33666
  }
33667
+ function capRenderedToolResult(result, args) {
33668
+ const limit = inspectMcpOutputLimit(result, args.maximum);
33669
+ if (limit.ok) return result;
33670
+ return {
33671
+ content: [{
33672
+ type: "text",
33673
+ text: formatMcpOutputLimitError({
33674
+ label: args.label,
33675
+ limit,
33676
+ setting: args.setting
33677
+ })
33678
+ }],
33679
+ isError: true,
33680
+ structuredContent: {
33681
+ code: "OUTPUT_LIMIT_EXCEEDED",
33682
+ maximumBytes: limit.maximum,
33683
+ observedBytes: limit.observed
33684
+ }
33685
+ };
33686
+ }
33687
+ function capRenderChartResult(result) {
33688
+ const limits = resolveMcpRenderOutputLimits();
33689
+ return capRenderedToolResult(result, {
33690
+ label: "Rendered chart",
33691
+ maximum: limits.maxRenderOutputBytes,
33692
+ setting: "MCP_MAX_RENDER_OUTPUT_BYTES"
33693
+ });
33694
+ }
33695
+ function capInteractiveWidgetResult(result) {
33696
+ const limits = resolveMcpRenderOutputLimits();
33697
+ return capRenderedToolResult(result, {
33698
+ label: "Interactive widget",
33699
+ maximum: limits.maxWidgetOutputBytes,
33700
+ setting: "MCP_MAX_WIDGET_OUTPUT_BYTES"
33701
+ });
33702
+ }
33703
+ var SURFACE_VERSION = `${schema.version || "3.0.0"}-ai`;
33704
+ function profileResult(result) {
33705
+ return { ...result, surfaceVersion: SURFACE_VERSION };
33706
+ }
32802
33707
  async function getSchemaHandler(args) {
32803
33708
  const component = args.component;
32804
33709
  if (!component) {
@@ -32809,7 +33714,7 @@ ${list.join(", ")}
32809
33714
 
32810
33715
  Components marked [renderable] can be rendered to SVG via renderChart (pass theme parameter for styled output). Others (Realtime*) require a browser environment.
32811
33716
 
32812
- For full agent context, read MCP resources: semiotic://schema, semiotic://components, semiotic://behavior-contracts, semiotic://system-prompt, semiotic://examples.
33717
+ For full agent context, read MCP resources: semiotic://schema, semiotic://components, semiotic://surface-manifest, semiotic://behavior-contracts, semiotic://system-prompt, semiotic://examples.
32813
33718
 
32814
33719
  All charts support CSS custom properties for theming (--semiotic-bg, --semiotic-text, --semiotic-grid, etc.) and <ThemeProvider>. Use COLOR_BLIND_SAFE_CATEGORICAL (import from semiotic/themes) for accessible color palettes.
32815
33720
 
@@ -32859,98 +33764,148 @@ async function suggestChartHandler(args) {
32859
33764
  }
32860
33765
  return { content, structuredContent: result };
32861
33766
  }
32862
- async function renderChartHandler(args) {
33767
+ async function renderChartHandler(args, context = {}) {
33768
+ const limits = context.limits ?? resolveMcpRenderExecutionLimits();
33769
+ const signal = context.signal;
32863
33770
  const component = args.component;
32864
33771
  const props = args.props ?? {};
32865
33772
  const theme = args.theme;
32866
33773
  const format = args.format || "svg";
32867
33774
  if (!component) {
32868
- return {
33775
+ return capRenderChartResult({
32869
33776
  content: [{ type: "text", text: `Missing 'component' field. Provide { component: '<name>', props: { ... } }. Available: ${componentNames.join(", ")}` }],
32870
33777
  isError: true
32871
- };
33778
+ });
32872
33779
  }
32873
33780
  if (!COMPONENT_REGISTRY[component]) {
32874
33781
  if (schemaByComponent[component]) {
32875
- return {
33782
+ return capRenderChartResult({
32876
33783
  content: [{ type: "text", text: `Component "${component}" is known but cannot be rendered via renderChart. It requires a browser/live environment. Renderable components: ${componentNames.join(", ")}` }],
32877
33784
  isError: true
32878
- };
33785
+ });
32879
33786
  }
32880
- return {
33787
+ return capRenderChartResult({
32881
33788
  content: [{ type: "text", text: `Unknown component "${component}". Available: ${componentNames.join(", ")}` }],
32882
33789
  isError: true
32883
- };
33790
+ });
33791
+ }
33792
+ let result;
33793
+ try {
33794
+ result = await runRenderStep(
33795
+ "render work",
33796
+ limits.maxRenderWorkMs,
33797
+ signal,
33798
+ () => renderHOCToSVG(component, props)
33799
+ );
33800
+ } catch (err) {
33801
+ if (isRenderExecutionError(err)) {
33802
+ return capRenderChartResult(renderExecutionErrorResult(err.message, err.code));
33803
+ }
33804
+ throw err;
32884
33805
  }
32885
- const result = renderHOCToSVG(component, props);
32886
33806
  if (result.error) {
32887
- return {
33807
+ return capRenderChartResult({
32888
33808
  content: [{ type: "text", text: result.error }],
32889
33809
  isError: true
32890
- };
33810
+ });
32891
33811
  }
32892
33812
  let svg = result.svg;
32893
33813
  let evidenceBlock = null;
32894
33814
  try {
32895
- const { svg: evidenceSvg, evidence } = (0, import_server2.renderChartWithEvidence)(component, props);
33815
+ const { svg: evidenceSvg, evidence } = await runRenderStep(
33816
+ "layout/render evidence",
33817
+ limits.maxRenderWorkMs,
33818
+ signal,
33819
+ () => (0, import_server2.renderChartWithEvidence)(component, props)
33820
+ );
32896
33821
  svg = evidenceSvg;
32897
33822
  evidenceBlock = {
32898
33823
  type: "text",
32899
33824
  text: `Render evidence:
32900
33825
  ${JSON.stringify(evidence, null, 2)}`
32901
33826
  };
32902
- } catch {
33827
+ } catch (err) {
33828
+ if (isRenderExecutionError(err)) {
33829
+ return capRenderChartResult(renderExecutionErrorResult(err.message, err.code));
33830
+ }
32903
33831
  evidenceBlock = {
32904
33832
  type: "text",
32905
33833
  text: `Render evidence: unavailable for ${component} (no server render config). The SVG above is the validated React render; mark-count / domain evidence is only produced for components with a server render path.`
32906
33834
  };
32907
33835
  }
32908
33836
  if (theme && Object.keys(theme).length > 0) {
32909
- const validVars = Object.entries(theme).filter(([k]) => k.startsWith("--semiotic-")).map(([k, v]) => `${k}: ${v}`).join("; ");
32910
- if (validVars) {
32911
- svg = svg.replace(/<svg([^>]*)>/, `<svg$1><style>:root { ${validVars} }</style>`);
33837
+ try {
33838
+ svg = await runRenderStep(
33839
+ "theme application",
33840
+ limits.maxRenderWorkMs,
33841
+ signal,
33842
+ () => {
33843
+ const isUnsafeCssValue = (v) => /<|>|{|}|@|expression\(|javascript:|url\(|\*\//i.test(v);
33844
+ const validVars = Object.entries(theme).filter(([k, v]) => k.startsWith("--semiotic-") && typeof v === "string" && !isUnsafeCssValue(v)).map(([k, v]) => `${k}: ${v}`).join("; ");
33845
+ if (!validVars) return svg;
33846
+ return svg.replace(/<svg([^>]*)>/, `<svg$1><style>:root { ${validVars} }</style>`);
33847
+ }
33848
+ );
33849
+ } catch (err) {
33850
+ if (isRenderExecutionError(err)) {
33851
+ return capRenderChartResult(renderExecutionErrorResult(err.message, err.code));
33852
+ }
33853
+ throw err;
32912
33854
  }
32913
33855
  }
32914
33856
  if (format === "png") {
32915
33857
  try {
32916
- const sharpMod = await Function('return import("sharp")')();
32917
- const sharpFn = sharpMod.default || sharpMod;
32918
- const pngBuffer = await sharpFn(Buffer.from(svg)).png().toBuffer();
33858
+ const pngBuffer = await runRenderStep(
33859
+ "PNG conversion",
33860
+ limits.maxPngConversionMs,
33861
+ signal,
33862
+ async () => {
33863
+ const sharpMod = await Function('return import("sharp")')();
33864
+ const sharpFn = sharpMod.default || sharpMod;
33865
+ return sharpFn(Buffer.from(svg)).png().toBuffer();
33866
+ }
33867
+ );
32919
33868
  const base643 = pngBuffer.toString("base64");
32920
- return {
33869
+ return capRenderChartResult({
32921
33870
  content: [
32922
- { type: "text", text: `data:image/png;base64,${base643}` },
33871
+ { type: "image", data: base643, mimeType: "image/png" },
32923
33872
  ...evidenceBlock ? [evidenceBlock] : []
32924
33873
  ]
32925
- };
33874
+ });
32926
33875
  } catch (err) {
32927
- if (err.code === "MODULE_NOT_FOUND" || err.code === "ERR_MODULE_NOT_FOUND") {
32928
- return {
33876
+ if (isRenderExecutionError(err)) {
33877
+ return capRenderChartResult(renderExecutionErrorResult(err.message, err.code));
33878
+ }
33879
+ const typedErr = err;
33880
+ if (typedErr.code === "MODULE_NOT_FOUND" || typedErr.code === "ERR_MODULE_NOT_FOUND") {
33881
+ return capRenderChartResult({
32929
33882
  content: [{ type: "text", text: `PNG output requires the 'sharp' package. Install it with: npm install sharp
32930
33883
 
32931
33884
  Falling back to SVG output:
32932
33885
 
32933
33886
  ${svg}` }]
32934
- };
33887
+ });
32935
33888
  }
32936
- return {
32937
- content: [{ type: "text", text: `PNG conversion failed: ${err.message}
33889
+ return capRenderChartResult({
33890
+ content: [{ type: "text", text: `PNG conversion failed: ${typedErr.message || "unknown error"}
32938
33891
 
32939
33892
  SVG output:
32940
33893
 
32941
33894
  ${svg}` }],
32942
33895
  isError: true
32943
- };
33896
+ });
32944
33897
  }
32945
33898
  }
32946
- return {
33899
+ return capRenderChartResult({
32947
33900
  content: [
32948
33901
  { type: "text", text: svg },
32949
33902
  ...evidenceBlock ? [evidenceBlock] : []
32950
33903
  ]
32951
- };
33904
+ });
32952
33905
  }
32953
- async function renderInteractiveChartHandler(args) {
33906
+ async function renderInteractiveChartHandler(args, context = {}) {
33907
+ const limits = context.limits ?? resolveMcpRenderExecutionLimits();
33908
+ const signal = context.signal;
32954
33909
  const component = args.component;
32955
33910
  const props = args.props ?? {};
32956
33911
  const rendered = await renderChartHandler({
@@ -32958,10 +33913,31 @@ async function renderInteractiveChartHandler(args) {
32958
33913
  props,
32959
33914
  theme: args.theme,
32960
33915
  format: "svg"
32961
- });
33916
+ }, context);
32962
33917
  if (rendered.isError) return rendered;
32963
- const svg = stripUnsafeSvg(rendered.content[0]?.text ?? "");
32964
- const evidence = parseRenderEvidence(rendered);
33918
+ const svgBlock = rendered.content.find(
33919
+ (block) => block.type === "text" && block.text.trimStart().startsWith("<")
33920
+ );
33921
+ let svg;
33922
+ try {
33923
+ svg = await runRenderStep(
33924
+ "interactive SVG sanitization",
33925
+ limits.maxInteractiveSanitizeMs,
33926
+ signal,
33927
+ () => sanitizeSvgForWidget(svgBlock?.text ?? "")
33928
+ );
33929
+ } catch (err) {
33930
+ if (isRenderExecutionError(err)) {
33931
+ return capInteractiveWidgetResult(renderExecutionErrorResult(err.message, err.code));
33932
+ }
33933
+ return capInteractiveWidgetResult({
33934
+ content: [{ type: "text", text: "Interactive SVG sanitization failed." }],
33935
+ isError: true
33936
+ });
33937
+ }
33938
+ const outputLimits = resolveMcpRenderOutputLimits();
33939
+ const evidence = createWidgetEvidencePreview(parseRenderEvidence(rendered), outputLimits);
33940
+ const dataPreview = createWidgetDataPreview(props, outputLimits);
32965
33941
  const title = chartTitleFromProps(component || "Semiotic chart", props);
32966
33942
  const datumCount = chartDatumCount(props);
32967
33943
  const summary = [
@@ -32969,7 +33945,7 @@ async function renderInteractiveChartHandler(args) {
32969
33945
  datumCount == null ? "No row count was inferred from props." : `${datumCount} input row${datumCount === 1 ? "" : "s"} available in the widget data drawer.`,
32970
33946
  "Use the widget controls to zoom, fit width, inspect data, and inspect render evidence."
32971
33947
  ].join(" ");
32972
- return {
33948
+ return capInteractiveWidgetResult({
32973
33949
  content: [{
32974
33950
  type: "text",
32975
33951
  text: `Rendered ${title} (${component}) as an interactive ChatGPT Apps widget.`
@@ -32982,15 +33958,14 @@ async function renderInteractiveChartHandler(args) {
32982
33958
  evidence
32983
33959
  },
32984
33960
  _meta: {
32985
- component,
33961
+ component: component ?? "SemioticChart",
32986
33962
  title,
32987
- props,
32988
- theme: args.theme ?? null,
33963
+ dataPreview,
32989
33964
  svg,
32990
33965
  evidence,
32991
33966
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
32992
33967
  }
32993
- };
33968
+ });
32994
33969
  }
32995
33970
  function filterUsageModeDiagnoses(component, usageMode, diagnoses) {
32996
33971
  if (dataRequiredForUsageMode(component, usageMode)) return diagnoses;
@@ -33053,6 +34028,26 @@ async function auditAccessibilityHandler(args) {
33053
34028
  isError: !result.ok
33054
34029
  };
33055
34030
  }
34031
+ async function auditMobileVisualizationHandler(args) {
34032
+ const component = args.component;
34033
+ const props = args.props ?? {};
34034
+ if (!component) {
34035
+ return {
34036
+ content: [{ type: "text", text: "Missing 'component' field. Provide { component: 'LineChart', props: { ... } }." }],
34037
+ isError: true
34038
+ };
34039
+ }
34040
+ const result = (0, import_ai3.auditMobileVisualization)(component, props, {
34041
+ viewportWidth: typeof args.viewportWidth === "number" ? args.viewportWidth : void 0,
34042
+ targetSize: typeof args.targetSize === "number" ? args.targetSize : void 0,
34043
+ inChartContainer: args.inChartContainer === true
34044
+ });
34045
+ return {
34046
+ content: [{ type: "text", text: (0, import_ai3.formatMobileVisualizationAudit)(result) }],
34047
+ // Block only on high-risk mobile issues; medium/low warnings remain advisory.
34048
+ isError: !result.ok
34049
+ };
34050
+ }
33056
34051
  async function reportIssueHandler(args) {
33057
34052
  const title = args.title;
33058
34053
  const body = args.body;
@@ -33275,6 +34270,74 @@ async function suggestChartsHandler(args) {
33275
34270
  structuredContent: { suggestions }
33276
34271
  };
33277
34272
  }
34273
+ var ALLOWED_TOKEN_TASK_INTENTS = [
34274
+ "precise-comparison",
34275
+ "frequency-reasoning",
34276
+ "probability-estimation",
34277
+ "risk-communication",
34278
+ "memory",
34279
+ "editorial-engagement",
34280
+ "public-explanation",
34281
+ "support-decision",
34282
+ "measure",
34283
+ "estimate probability",
34284
+ "understand risk",
34285
+ "remember",
34286
+ "decide"
34287
+ ];
34288
+ function isTokenTaskIntent(value) {
34289
+ return ALLOWED_TOKEN_TASK_INTENTS.includes(value);
34290
+ }
34291
+ async function suggestTokenEncodingHandler(args) {
34292
+ if (!args.taskIntent) {
34293
+ return {
34294
+ content: [{
34295
+ type: "text",
34296
+ text: "Missing 'taskIntent'. Provide a token task such as 'estimate probability', 'understand risk', 'remember', 'measure', or 'decide'."
34297
+ }],
34298
+ isError: true
34299
+ };
34300
+ }
34301
+ if (!isTokenTaskIntent(args.taskIntent)) {
34302
+ return {
34303
+ content: [{
34304
+ type: "text",
34305
+ text: `Invalid 'taskIntent': "${args.taskIntent}". Expected one of: ${ALLOWED_TOKEN_TASK_INTENTS.join(", ")}.`
34306
+ }],
34307
+ isError: true
34308
+ };
34309
+ }
34310
+ const taskIntent = args.taskIntent;
34311
+ const suggestion = (0, import_ai3.suggestTokenEncoding)({
34312
+ taskIntent,
34313
+ dataType: args.dataType,
34314
+ audience: args.audience,
34315
+ precisionNeed: args.precisionNeed,
34316
+ availableSpace: args.availableSpace,
34317
+ concreteEntity: args.concreteEntity
34318
+ });
34319
+ const capabilityIntents = (0, import_ai3.tokenTaskIntentToCapabilityIntents)(taskIntent);
34320
+ const warnings = suggestion.warnings.length ? `
34321
+ Warnings:
34322
+ ${suggestion.warnings.map((warning) => `- [${warning.code}] ${warning.message}`).join("\n")}` : "";
34323
+ const encoding = suggestion.tokenEncoding ? `
34324
+ Encoding:
34325
+ ${JSON.stringify(suggestion.tokenEncoding, null, 2)}` : "";
34326
+ return {
34327
+ content: [{
34328
+ type: "text",
34329
+ text: [
34330
+ `Recommended token encoding: ${suggestion.recommendedEncoding}`,
34331
+ `Rationale: ${suggestion.rationale}`,
34332
+ `Capability intents: ${capabilityIntents.join(", ")}`,
34333
+ encoding.trim(),
34334
+ warnings.trim(),
34335
+ `Alternatives: ${suggestion.alternatives.join(", ")}`
34336
+ ].filter(Boolean).join("\n\n")
34337
+ }],
34338
+ structuredContent: { suggestion, capabilityIntents }
34339
+ };
34340
+ }
33278
34341
  async function suggestStreamChartsHandler(args) {
33279
34342
  const { schema: schema2, intent, maxResults } = args;
33280
34343
  const intentArg = Array.isArray(intent) ? intent : intent ? [intent] : void 0;
@@ -33437,16 +34500,124 @@ async function groundChartHandler(args) {
33437
34500
  structuredContent: grounding
33438
34501
  };
33439
34502
  }
34503
+ function compactPublicChartProps(props) {
34504
+ const compact = { ...props };
34505
+ delete compact.data;
34506
+ delete compact.nodes;
34507
+ delete compact.edges;
34508
+ return compact;
34509
+ }
34510
+ function compactPublicSuggestion(suggestion) {
34511
+ return { ...suggestion, props: compactPublicChartProps(suggestion.props) };
34512
+ }
34513
+ async function createChartHandler(args, context = {}) {
34514
+ const intent = Array.isArray(args.intent) ? args.intent : args.intent ? [args.intent] : void 0;
34515
+ const suggestions = (0, import_ai3.suggestCharts)(args.data, { intent, audience: args.audience, maxResults: 40 }).filter((suggestion) => metadataForComponent(suggestion.component).renderable).slice(0, 8);
34516
+ const selected = args.component ? suggestions.find((suggestion) => suggestion.component === args.component) : suggestions[0];
34517
+ if (!selected) {
34518
+ return {
34519
+ content: [{ type: "text", text: "No renderable Semiotic chart was suggested for this data. Use getChartSchema for code-level guidance." }],
34520
+ isError: true,
34521
+ structuredContent: profileResult({
34522
+ status: "no-suggestion",
34523
+ suggestions: suggestions.map(compactPublicSuggestion),
34524
+ dataRowCount: args.data.length
34525
+ })
34526
+ };
34527
+ }
34528
+ const props = { data: args.data, ...selected.props, ...args.props };
34529
+ const publicProps = compactPublicChartProps(props);
34530
+ const publicSuggestion = compactPublicSuggestion(selected);
34531
+ const diagnosis = (0, import_ai3.diagnoseConfig)(selected.component, props);
34532
+ const blocking = diagnosis.diagnoses.filter((item) => item.severity === "error");
34533
+ if (blocking.length) {
34534
+ return {
34535
+ content: [{ type: "text", text: `Selected ${selected.component}, but blocking diagnostics require repair before rendering.` }],
34536
+ isError: true,
34537
+ structuredContent: profileResult({
34538
+ status: "blocked",
34539
+ component: selected.component,
34540
+ props: publicProps,
34541
+ dataRowCount: args.data.length,
34542
+ suggestion: publicSuggestion,
34543
+ diagnostics: diagnosis.diagnoses
34544
+ })
34545
+ };
34546
+ }
34547
+ const rendered = await renderInteractiveChartHandler({ component: selected.component, props, theme: args.theme }, context);
34548
+ if (rendered.isError) {
34549
+ return {
34550
+ ...rendered,
34551
+ structuredContent: profileResult({
34552
+ status: "blocked",
34553
+ component: selected.component,
34554
+ props: publicProps,
34555
+ dataRowCount: args.data.length,
34556
+ suggestion: publicSuggestion,
34557
+ diagnostics: diagnosis.diagnoses,
34558
+ render: rendered.structuredContent ?? null
34559
+ })
34560
+ };
34561
+ }
34562
+ const output = rendered.structuredContent ?? {};
34563
+ return {
34564
+ ...rendered,
34565
+ structuredContent: profileResult({
34566
+ status: "render-proven",
34567
+ component: selected.component,
34568
+ props: publicProps,
34569
+ dataRowCount: args.data.length,
34570
+ suggestion: publicSuggestion,
34571
+ diagnostics: diagnosis.diagnoses,
34572
+ render: output
34573
+ })
34574
+ };
34575
+ }
34576
+ async function improveChartHandler(args) {
34577
+ const data = args.data ?? (Array.isArray(args.props.data) ? args.props.data : []);
34578
+ const intent = Array.isArray(args.intent) ? args.intent : args.intent ? [args.intent] : void 0;
34579
+ const diagnosis = (0, import_ai3.diagnoseConfig)(args.component, args.props);
34580
+ const repair = (0, import_ai3.repairChartConfig)(args.component, data, { intent });
34581
+ const capability = (0, import_ai3.getCapability)(args.component);
34582
+ const variants = capability ? (0, import_ai3.proposeVariant)(args.component, capability, { profile: (0, import_ai3.profileData)(data), intent }) : [];
34583
+ return {
34584
+ content: [{ type: "text", text: `Improvement analysis for ${args.component}: ${diagnosis.diagnoses.length} diagnosis item(s), repair status ${repair.status}, ${variants.length} variant proposal(s).` }],
34585
+ structuredContent: profileResult({ status: repair.status === "ok" ? "reviewed" : "repair-needed", component: args.component, diagnostics: diagnosis.diagnoses, repair, variants })
34586
+ };
34587
+ }
34588
+ async function explainChartHandler(args) {
34589
+ const grounded = await groundChartHandler(args);
34590
+ return {
34591
+ ...grounded,
34592
+ structuredContent: grounded.structuredContent ? profileResult({ status: "grounded", grounding: grounded.structuredContent }) : void 0
34593
+ };
34594
+ }
34595
+ async function auditChartHandler(args) {
34596
+ const diagnosis = (0, import_ai3.diagnoseConfig)(args.component, args.props);
34597
+ const accessibility = (0, import_ai3.auditAccessibility)(args.component, args.props, { inChartContainer: true, describe: true, navigable: true });
34598
+ const mobile = (0, import_ai3.auditMobileVisualization)(args.component, args.props, { viewportWidth: args.viewportWidth, inChartContainer: true });
34599
+ const blocking = diagnosis.diagnoses.some((item) => item.severity === "error") || !accessibility.ok || !mobile.ok;
34600
+ return {
34601
+ content: [{ type: "text", text: `Audit for ${args.component}: ${blocking ? "blocking findings need attention" : "no blocking findings"}.` }],
34602
+ isError: blocking,
34603
+ structuredContent: profileResult({ status: blocking ? "findings" : "passed", component: args.component, diagnostics: diagnosis.diagnoses, accessibility, mobile })
34604
+ };
34605
+ }
33440
34606
  var READ_ONLY_TOOL_ANNOTATIONS = {
33441
34607
  readOnlyHint: true,
33442
34608
  destructiveHint: false,
33443
34609
  idempotentHint: true,
33444
34610
  openWorldHint: false
33445
34611
  };
33446
- function createServer2() {
34612
+ function createServer2(profile = "developer", options = {}) {
34613
+ const serverRenderContext = {
34614
+ signal: options.signal,
34615
+ limits: options.limits ?? resolveMcpRenderExecutionLimits()
34616
+ };
33447
34617
  const srv = new McpServer({
33448
34618
  name: "semiotic",
33449
- version: schema.version || "3.0.0"
34619
+ version: schema.version || "3.0.0",
34620
+ description: "Deterministic Semiotic chart selection, validation, rendering, and non-visual chart grounding. Use suggestCharts, getSchema, diagnoseConfig, and renderChart in that order for static chart generation."
33450
34621
  });
33451
34622
  srv.registerResource(
33452
34623
  "semiotic-schema",
@@ -33468,6 +34639,16 @@ function createServer2() {
33468
34639
  },
33469
34640
  (uri) => textResource(uri, "application/json", componentIndexJSON())
33470
34641
  );
34642
+ srv.registerResource(
34643
+ "semiotic-surface-manifest",
34644
+ "semiotic://surface-manifest",
34645
+ {
34646
+ title: "Semiotic AI Surface Manifest",
34647
+ description: "Generated inventory of schema components, AI exports, MCP renderability, tools, resources, and prompts.",
34648
+ mimeType: "application/json"
34649
+ },
34650
+ (uri) => textResource(uri, "application/json", readAIFile("surface-manifest.json"))
34651
+ );
33471
34652
  srv.registerResource(
33472
34653
  "semiotic-behavior-contracts",
33473
34654
  "semiotic://behavior-contracts",
@@ -33583,6 +34764,51 @@ function createServer2() {
33583
34764
  "Return the smallest safe fix first, then mention any follow-up cleanup or issue-reporting step."
33584
34765
  ].join("\n"))
33585
34766
  );
34767
+ if (profile === "public") {
34768
+ srv.registerTool("createChart", {
34769
+ title: "Create and prove a chart",
34770
+ description: "Select, validate, diagnose, render, and prove a static-data Semiotic chart. This is the default public workflow.",
34771
+ inputSchema: {
34772
+ data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).min(1),
34773
+ intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional(),
34774
+ audience: external_exports3.object({ name: external_exports3.string().optional(), receptionModality: external_exports3.enum(["visual", "screen-reader", "sonified", "agent"]).optional() }).passthrough().optional(),
34775
+ component: external_exports3.string().optional().describe("Optional chart preference; the fit-ranked result remains authoritative."),
34776
+ props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Optional props to merge over the selected chart recipe."),
34777
+ theme: external_exports3.record(external_exports3.string(), external_exports3.string()).optional()
34778
+ },
34779
+ outputSchema: { status: external_exports3.enum(["render-proven", "blocked", "no-suggestion"]), component: external_exports3.string().optional(), surfaceVersion: external_exports3.string() },
34780
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
34781
+ _meta: { ui: { resourceUri: SEMIOTIC_CHART_WIDGET_URI }, "openai/outputTemplate": SEMIOTIC_CHART_WIDGET_URI }
34782
+ }, (args) => createChartHandler(args, serverRenderContext));
34783
+ srv.registerTool("improveChart", {
34784
+ title: "Improve an existing chart",
34785
+ description: "Diagnose a chart configuration, assess data fit, and propose repairs or variants.",
34786
+ inputSchema: { component: external_exports3.string(), props: external_exports3.record(external_exports3.string(), external_exports3.unknown()), data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).optional(), intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional() },
34787
+ outputSchema: { status: external_exports3.enum(["reviewed", "repair-needed"]), component: external_exports3.string(), surfaceVersion: external_exports3.string() },
34788
+ annotations: READ_ONLY_TOOL_ANNOTATIONS
34789
+ }, improveChartHandler);
34790
+ srv.registerTool("explainChart", {
34791
+ title: "Explain a chart without pixels",
34792
+ description: "Return reader grounding: chart description, communicative intent, and navigable data structure.",
34793
+ inputSchema: { component: external_exports3.string(), props: external_exports3.record(external_exports3.string(), external_exports3.unknown()) },
34794
+ outputSchema: { status: external_exports3.literal("grounded"), grounding: external_exports3.record(external_exports3.string(), external_exports3.unknown()), surfaceVersion: external_exports3.string() },
34795
+ annotations: READ_ONLY_TOOL_ANNOTATIONS
34796
+ }, explainChartHandler);
34797
+ srv.registerTool("auditChart", {
34798
+ title: "Audit chart quality and accessibility",
34799
+ description: "Run design diagnostics plus accessibility and mobile audits, returning prioritized structured findings.",
34800
+ inputSchema: { component: external_exports3.string(), props: external_exports3.record(external_exports3.string(), external_exports3.unknown()), viewportWidth: external_exports3.number().int().min(240).max(1600).optional() },
34801
+ outputSchema: { status: external_exports3.enum(["passed", "findings"]), component: external_exports3.string(), surfaceVersion: external_exports3.string() },
34802
+ annotations: READ_ONLY_TOOL_ANNOTATIONS
34803
+ }, auditChartHandler);
34804
+ srv.registerTool("getChartSchema", {
34805
+ title: "Get a chart schema",
34806
+ description: "Return canonical Semiotic prop-schema guidance for code editing and advanced configuration.",
34807
+ inputSchema: { component: external_exports3.string().optional() },
34808
+ annotations: READ_ONLY_TOOL_ANNOTATIONS
34809
+ }, getSchemaHandler);
34810
+ return srv;
34811
+ }
33586
34812
  srv.tool(
33587
34813
  "getSchema",
33588
34814
  `Return the prop schema for a Semiotic chart component. Pass { component: '<name>' } to get its props, or omit component to list all available components. Components marked [renderable] can be passed to renderChart for static SVG output.`,
@@ -33613,15 +34839,15 @@ function createServer2() {
33613
34839
  );
33614
34840
  srv.tool(
33615
34841
  "renderChart",
33616
- `Render a Semiotic chart to static SVG or PNG. This is a static snapshot path: props must include data immediately, and ref/push-mode charts cannot be rendered through this tool. Returns SVG string (default) or Base64-encoded PNG image, plus a "Render evidence" JSON block (mark counts by type, resolved axis domains, empty flag, annotation count, accessible name) \u2014 read the evidence instead of parsing the SVG to verify the chart actually rendered data marks. Optionally pass theme CSS custom properties (--semiotic-bg, --semiotic-text, etc.) to style the output. PNG requires the 'sharp' package to be installed. Available components: ${componentNames.join(", ")}.`,
34842
+ `Render a Semiotic chart to static SVG or PNG. This is a static snapshot path: props must include data immediately, and ref/push-mode charts cannot be rendered through this tool. Returns SVG text by default or an image/png artifact when format='png', plus a "Render evidence" JSON block (mark counts by type, resolved axis domains, empty flag, annotation count, accessible name) \u2014 read the evidence instead of parsing the SVG to verify the chart actually rendered data marks. Optionally pass theme CSS custom properties (--semiotic-bg, --semiotic-text, etc.) to style the output. PNG requires the 'sharp' package to be installed. Available components: ${componentNames.join(", ")}.`,
33617
34843
  {
33618
34844
  component: external_exports3.string().describe("Chart component name, e.g. 'LineChart', 'BarChart'"),
33619
34845
  props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Chart props object, e.g. { data: [...], xAccessor: 'x' }."),
33620
34846
  theme: external_exports3.record(external_exports3.string(), external_exports3.string()).optional().describe("CSS custom properties for theming, e.g. { '--semiotic-bg': '#1a1a2e', '--semiotic-text': '#ededed' }. Only --semiotic-* variables are applied."),
33621
- format: external_exports3.enum(["svg", "png"]).optional().describe("Output format: 'svg' (default) returns SVG markup, 'png' returns a Base64-encoded PNG image. PNG requires the 'sharp' package.")
34847
+ format: external_exports3.enum(["svg", "png"]).optional().describe("Output format: 'svg' (default) returns SVG markup, 'png' returns image/png artifact data. PNG requires the 'sharp' package.")
33622
34848
  },
33623
34849
  READ_ONLY_TOOL_ANNOTATIONS,
33624
- renderChartHandler
34850
+ (args) => renderChartHandler(args, serverRenderContext)
33625
34851
  );
33626
34852
  srv.registerTool(
33627
34853
  "renderInteractiveChart",
@@ -33653,7 +34879,7 @@ function createServer2() {
33653
34879
  "openai/toolInvocation/invoked": "Rendered Semiotic chart."
33654
34880
  }
33655
34881
  },
33656
- renderInteractiveChartHandler
34882
+ (args) => renderInteractiveChartHandler(args, serverRenderContext)
33657
34883
  );
33658
34884
  srv.tool(
33659
34885
  "diagnoseConfig",
@@ -33679,6 +34905,19 @@ function createServer2() {
33679
34905
  READ_ONLY_TOOL_ANNOTATIONS,
33680
34906
  auditAccessibilityHandler
33681
34907
  );
34908
+ srv.tool(
34909
+ "auditMobileVisualization",
34910
+ "Audit a Semiotic chart configuration for mobile visualization risks. Use before generating phone-sized charts or when adapting a desktop chart to mobile. Flags fixed desktop widths, rough mark-density overload, hover-only detail, small touch targets, complex gestures without controls, legend dependence, annotation overload, and missing mobile transformation hints. Static analysis only: still verify rendered charts at phone widths.",
34911
+ {
34912
+ component: external_exports3.string().describe("Chart component name, e.g. 'LineChart', 'Scatterplot', or 'BarChart'."),
34913
+ props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Chart props/config to audit."),
34914
+ viewportWidth: external_exports3.number().int().min(240).max(1600).optional().describe("Mobile viewport width in CSS pixels. Defaults to 390."),
34915
+ targetSize: external_exports3.number().int().min(24).max(80).optional().describe("Desired comfortable touch target size in CSS pixels. Defaults to 44."),
34916
+ inChartContainer: external_exports3.boolean().optional().describe("Whether the chart is wrapped in ChartContainer or an equivalent summary/control surface.")
34917
+ },
34918
+ READ_ONLY_TOOL_ANNOTATIONS,
34919
+ auditMobileVisualizationHandler
34920
+ );
33682
34921
  srv.tool(
33683
34922
  "reportIssue",
33684
34923
  "Generate a GitHub issue URL for Semiotic bug reports or feature requests. Returns a URL the user can open to submit. For rendering bugs, include the component name, props summary, and any diagnoseConfig output in the body.",
@@ -33710,14 +34949,25 @@ function createServer2() {
33710
34949
  READ_ONLY_TOOL_ANNOTATIONS,
33711
34950
  interrogateChartHandler
33712
34951
  );
33713
- srv.tool(
34952
+ srv.registerTool(
33714
34953
  "groundChart",
33715
- "Build the agent-reader grounding payload for a Semiotic chart: the layered L1\u2013L3 natural-language description, the L4 communicative-act sentence (what the chart is asking the reader to do \u2014 'this is an alerting chart; the spike warrants a closer look'), and a structured navigation tree (chart \u2192 axes/series \u2192 datum). This is the documented thing an LLM reads to interpret a chart faithfully without seeing the pixels \u2014 the reader-side complement to a capability descriptor. The L4 act is resolved from the chart's registered capability. Returns prose plus the full structured payload (description/intent/structure/text).",
33716
34954
  {
33717
- component: external_exports3.string().describe("Chart component name, e.g. 'LineChart'"),
33718
- props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).describe("The full chart props including data")
34955
+ title: "Ground a Semiotic chart for a non-visual reader",
34956
+ description: "Build the agent-reader grounding payload for a Semiotic chart: the layered L1\u2013L3 natural-language description, the L4 communicative-act sentence (what the chart is asking the reader to do), and a structured navigation tree (chart \u2192 axes/series \u2192 datum). Use this to interpret a chart faithfully without pixels.",
34957
+ inputSchema: {
34958
+ component: external_exports3.string().describe("Chart component name, e.g. 'LineChart'"),
34959
+ props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).describe("The full chart props including data")
34960
+ },
34961
+ outputSchema: {
34962
+ component: external_exports3.string(),
34963
+ description: external_exports3.record(external_exports3.string(), external_exports3.unknown()),
34964
+ intent: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional(),
34965
+ structure: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional(),
34966
+ physics: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional(),
34967
+ text: external_exports3.string()
34968
+ },
34969
+ annotations: READ_ONLY_TOOL_ANNOTATIONS
33719
34970
  },
33720
- READ_ONLY_TOOL_ANNOTATIONS,
33721
34971
  groundChartHandler
33722
34972
  );
33723
34973
  srv.tool(
@@ -33753,6 +35003,20 @@ function createServer2() {
33753
35003
  READ_ONLY_TOOL_ANNOTATIONS,
33754
35004
  suggestDashboardHandler
33755
35005
  );
35006
+ srv.tool(
35007
+ "suggestTokenEncoding",
35008
+ "Recommend a semantic token / ISOTYPE encoding for a reader task. Use before drawing repeated dots, icons, glyphs, natural-frequency grids, quantile dotplots, or hybrid bar-token views. Returns the recommended tokenEncoding, warnings, alternatives, and matching suggestCharts capability intents. Accepts canonical token intents (precise-comparison, probability-estimation, risk-communication, memory, support-decision, etc.) and friendly aliases (measure, estimate probability, understand risk, remember, decide).",
35009
+ {
35010
+ taskIntent: external_exports3.string().describe("Reader task intent, e.g. 'estimate probability', 'understand risk', 'remember', 'measure', 'decide', or canonical token intents like 'probability-estimation'."),
35011
+ dataType: external_exports3.enum(["count", "measure", "distribution", "probability", "risk", "category"]).optional().describe("Data shape or meaning behind the tokenized view."),
35012
+ audience: external_exports3.enum(["expert", "general-public", "internal"]).optional().describe("Audience for the recommendation."),
35013
+ precisionNeed: external_exports3.enum(["low", "medium", "high"]).optional().describe("How much exact magnitude reading matters."),
35014
+ availableSpace: external_exports3.enum(["small", "medium", "large"]).optional().describe("Space budget for visible tokens."),
35015
+ concreteEntity: external_exports3.string().optional().describe("Concrete icon/glyph concept, e.g. person, bus, server. Becomes tokenEncoding.icon when useful.")
35016
+ },
35017
+ READ_ONLY_TOOL_ANNOTATIONS,
35018
+ suggestTokenEncodingHandler
35019
+ );
33756
35020
  srv.tool(
33757
35021
  "suggestStretchCharts",
33758
35022
  "Recommend literacy-growth chart picks for a dataset given an AudienceProfile. Returns charts the data supports but the audience is unfamiliar with (familiarity \u2264 3, or \u2264 4 at exposureLevel 2), each paired with the familiar chart it could substitute for and a rationale. Use when the consumer wants to gently expose users to less familiar but more analytically appropriate visualizations.",
@@ -33778,16 +35042,27 @@ function createServer2() {
33778
35042
  READ_ONLY_TOOL_ANNOTATIONS,
33779
35043
  suggestStretchChartsHandler
33780
35044
  );
33781
- srv.tool(
35045
+ srv.registerTool(
33782
35046
  "repairChartConfig",
33783
- "Validate that a chart component is a sensible choice for a dataset, and if not, propose alternatives that fit. Use when a user asks for a specific chart and you want to confirm it's appropriate, or when you've drafted a config and want to verify it. Returns either ok (no change needed), alternative (chart doesn't fit; here are ranked replacements with rationale), or unknown (no capability registered).",
33784
35047
  {
33785
- component: external_exports3.string().describe("Chart component name to validate, e.g. 'PieChart'"),
33786
- data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).describe("Row data \u2014 array of objects."),
33787
- intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional().describe("User intent \u2014 informs ranking of alternatives when the chart doesn't fit."),
33788
- maxAlternatives: external_exports3.number().int().min(1).max(10).optional().describe("Cap on alternatives returned (default 3).")
35048
+ title: "Repair an unsuitable chart choice",
35049
+ description: "Validate that a chart component is a sensible choice for a dataset, and if not, propose ranked alternatives that fit. Returns a structured status of ok, alternative, or unknown.",
35050
+ inputSchema: {
35051
+ component: external_exports3.string().describe("Chart component name to validate, e.g. 'PieChart'"),
35052
+ data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).describe("Row data \u2014 array of objects."),
35053
+ intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional().describe("User intent \u2014 informs ranking of alternatives when the chart doesn't fit."),
35054
+ maxAlternatives: external_exports3.number().int().min(1).max(10).optional().describe("Cap on alternatives returned (default 3).")
35055
+ },
35056
+ outputSchema: {
35057
+ status: external_exports3.enum(["ok", "alternative", "unknown"]),
35058
+ component: external_exports3.string(),
35059
+ reason: external_exports3.string().optional(),
35060
+ alternatives: external_exports3.array(external_exports3.unknown()).optional(),
35061
+ profile: external_exports3.record(external_exports3.string(), external_exports3.unknown()),
35062
+ repairs: external_exports3.array(external_exports3.string()).optional()
35063
+ },
35064
+ annotations: READ_ONLY_TOOL_ANNOTATIONS
33789
35065
  },
33790
- READ_ONLY_TOOL_ANNOTATIONS,
33791
35066
  repairChartConfigHandler
33792
35067
  );
33793
35068
  srv.tool(
@@ -33817,44 +35092,137 @@ function createServer2() {
33817
35092
  READ_ONLY_TOOL_ANNOTATIONS,
33818
35093
  proposeChartVariantsHandler
33819
35094
  );
33820
- srv.tool(
35095
+ srv.registerTool(
33821
35096
  "suggestCharts",
33822
- "Recommend Semiotic charts for a dataset using heuristic capability descriptors. Each chart declares which data shapes it serves and which intents (trend, compare-categories, distribution, correlation, part-to-whole, etc.) it answers \u2014 the engine returns a ranked list with scores, reasons, caveats, and ready-to-use props. Heuristic only; no LLM call. Use the result as structured context when answering 'what chart should I use?' or generating chart code.",
33823
35097
  {
33824
- data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).describe("Row data \u2014 array of objects."),
33825
- intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional().describe("Ranking intent. One of: trend, compare-series, compare-categories, rank, part-to-whole, distribution, correlation, flow, hierarchy, geo, outlier-detection, composition-over-time, change-detection. Custom intents accepted."),
33826
- maxResults: external_exports3.number().int().min(1).max(40).optional().describe("Cap on suggestions returned (default 8)."),
33827
- allow: external_exports3.array(external_exports3.string()).optional().describe("Restrict to these component names."),
33828
- deny: external_exports3.array(external_exports3.string()).optional().describe("Exclude these component names."),
33829
- audience: external_exports3.object({
33830
- name: external_exports3.string().optional(),
33831
- familiarity: external_exports3.record(external_exports3.string(), external_exports3.number()).optional(),
33832
- targets: external_exports3.record(
33833
- external_exports3.string(),
33834
- external_exports3.object({
33835
- direction: external_exports3.enum(["increase", "decrease"]),
33836
- weight: external_exports3.number().int().min(1).max(3).optional(),
33837
- reason: external_exports3.string().optional()
33838
- })
33839
- ).optional(),
33840
- exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional(),
33841
- receptionModality: external_exports3.enum(["visual", "screen-reader", "sonified", "agent"]).optional().describe("Reception channel. A non-visual value down-ranks charts the audience can't receive in that channel (e.g. a many-slice pie for a screen reader) and adds receivability caveats.")
33842
- }).optional().describe("Audience profile \u2014 familiarity, adoption targets, exposure level, and reception modality.")
35098
+ title: "Recommend Semiotic charts",
35099
+ description: "Recommend Semiotic charts for a dataset using heuristic capability descriptors. Returns ranked, structured suggestions with scores, reasons, caveats, and ready-to-use props; no LLM call is made.",
35100
+ inputSchema: {
35101
+ data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).describe("Row data \u2014 array of objects."),
35102
+ intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional().describe("Ranking intent. One of: trend, compare-series, compare-categories, rank, part-to-whole, distribution, correlation, flow, hierarchy, geo, outlier-detection, composition-over-time, change-detection. Custom intents accepted."),
35103
+ maxResults: external_exports3.number().int().min(1).max(40).optional().describe("Cap on suggestions returned (default 8)."),
35104
+ allow: external_exports3.array(external_exports3.string()).optional().describe("Restrict to these component names."),
35105
+ deny: external_exports3.array(external_exports3.string()).optional().describe("Exclude these component names."),
35106
+ audience: external_exports3.object({
35107
+ name: external_exports3.string().optional(),
35108
+ familiarity: external_exports3.record(external_exports3.string(), external_exports3.number()).optional(),
35109
+ targets: external_exports3.record(
35110
+ external_exports3.string(),
35111
+ external_exports3.object({
35112
+ direction: external_exports3.enum(["increase", "decrease"]),
35113
+ weight: external_exports3.number().int().min(1).max(3).optional(),
35114
+ reason: external_exports3.string().optional()
35115
+ })
35116
+ ).optional(),
35117
+ exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional(),
35118
+ receptionModality: external_exports3.enum(["visual", "screen-reader", "sonified", "agent"]).optional().describe("Reception channel. A non-visual value down-ranks charts the audience can't receive in that channel and adds receivability caveats.")
35119
+ }).optional().describe("Audience profile \u2014 familiarity, adoption targets, exposure level, and reception modality.")
35120
+ },
35121
+ outputSchema: { suggestions: external_exports3.array(external_exports3.unknown()) },
35122
+ annotations: READ_ONLY_TOOL_ANNOTATIONS
33843
35123
  },
33844
- READ_ONLY_TOOL_ANNOTATIONS,
33845
35124
  suggestChartsHandler
33846
35125
  );
33847
35126
  return srv;
33848
35127
  }
33849
35128
  var cliArgs = process.argv.slice(2);
33850
35129
  var httpMode = cliArgs.includes("--http");
35130
+ var profileFlagIndex = cliArgs.indexOf("--profile");
35131
+ var requestedProfile = profileFlagIndex !== -1 ? cliArgs[profileFlagIndex + 1] : process.env.MCP_TOOL_PROFILE;
35132
+ var toolProfile = requestedProfile === "public" ? "public" : "developer";
33851
35133
  var portFlagIndex = cliArgs.indexOf("--port");
33852
35134
  var parsedPort = portFlagIndex !== -1 && cliArgs[portFlagIndex + 1] != null ? parseInt(cliArgs[portFlagIndex + 1], 10) : NaN;
33853
35135
  var port = Number.isFinite(parsedPort) ? parsedPort : 3001;
35136
+ var host = resolveHTTPListenHost(cliArgs);
35137
+ function operationLimitForMcpRequest(body, limits) {
35138
+ const requests = Array.isArray(body) ? body : [body];
35139
+ for (const candidate of requests) {
35140
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
35141
+ const request = candidate;
35142
+ if (request.method !== "tools/call") continue;
35143
+ if (!request.params || typeof request.params !== "object" || Array.isArray(request.params)) continue;
35144
+ const params = request.params;
35145
+ if (!("arguments" in params)) continue;
35146
+ const result = inspectMcpOperationInput(params.arguments, limits);
35147
+ if (!result.ok) return result;
35148
+ }
35149
+ return null;
35150
+ }
33854
35151
  async function main() {
33855
35152
  if (httpMode) {
33856
35153
  const allowedHosts = (process.env.MCP_ALLOWED_HOSTS || "").split(",").map((h) => h.trim().toLowerCase()).filter(Boolean);
35154
+ const allowedOrigins = (process.env.MCP_ALLOWED_ORIGINS || "").split(",").map((o) => o.trim().toLowerCase()).filter(Boolean);
35155
+ const parsedMaxBody = parseInt(process.env.MCP_MAX_BODY_BYTES || "", 10);
35156
+ const maxBodyBytes = Number.isFinite(parsedMaxBody) && parsedMaxBody > 0 ? parsedMaxBody : 4194304;
35157
+ const operationLimits = resolveMcpOperationLimits();
33857
35158
  const openaiAppsChallengeToken = (process.env.OPENAI_APPS_CHALLENGE_TOKEN || "").trim();
35159
+ const renderExecutionLimits = resolveMcpRenderExecutionLimits();
35160
+ const requestLimits = resolveMcpRequestLimits();
35161
+ const requestLimiter = createMcpRequestLimiter(requestLimits);
35162
+ const protocolVersions = (process.env.MCP_SUPPORTED_PROTOCOL_VERSIONS || "").split(",").map((version2) => version2.trim()).filter(Boolean);
35163
+ const protocolVersion = protocolVersions[0] || DEFAULT_MCP_SUPPORTED_PROTOCOL_VERSION;
35164
+ const authToken = (process.env.MCP_AUTH_TOKEN || "").trim();
35165
+ const authScheme = (process.env.MCP_AUTH_SCHEME || "Bearer").trim() || "Bearer";
35166
+ const readJsonBodyWithLimit = (req) => new Promise((resolve2) => {
35167
+ let size = 0;
35168
+ let done = false;
35169
+ const chunks = [];
35170
+ const finish = (result) => {
35171
+ if (done) return;
35172
+ done = true;
35173
+ resolve2(result);
35174
+ };
35175
+ req.on("data", (chunk) => {
35176
+ if (done) return;
35177
+ size += chunk.length;
35178
+ if (size > maxBodyBytes) {
35179
+ finish({
35180
+ ok: false,
35181
+ status: 413,
35182
+ code: -32600,
35183
+ message: "Request body too large",
35184
+ reason: "request_body_too_large"
35185
+ });
35186
+ return;
35187
+ }
35188
+ chunks.push(chunk);
35189
+ });
35190
+ req.on("end", () => {
35191
+ if (done) return;
35192
+ const raw = Buffer.concat(chunks).toString("utf-8");
35193
+ if (!raw) return finish({ ok: true, body: void 0, bodyBytes: size });
35194
+ try {
35195
+ const body = JSON.parse(raw);
35196
+ const operationLimit = operationLimitForMcpRequest(body, operationLimits);
35197
+ if (operationLimit && !operationLimit.ok) {
35198
+ finish({
35199
+ ok: false,
35200
+ status: 413,
35201
+ code: -32602,
35202
+ message: formatMcpOperationLimitError(operationLimit),
35203
+ reason: "operation_limit"
35204
+ });
35205
+ return;
35206
+ }
35207
+ finish({ ok: true, body, bodyBytes: size });
35208
+ } catch {
35209
+ finish({
35210
+ ok: false,
35211
+ status: 400,
35212
+ code: -32600,
35213
+ message: "Invalid JSON body",
35214
+ reason: "invalid_json"
35215
+ });
35216
+ }
35217
+ });
35218
+ req.on("error", () => finish({
35219
+ ok: false,
35220
+ status: 400,
35221
+ code: -32600,
35222
+ message: "Request stream error",
35223
+ reason: "request_stream_error"
35224
+ }));
35225
+ });
33858
35226
  const healthBody = () => JSON.stringify({
33859
35227
  status: "ok",
33860
35228
  name: "semiotic-mcp",
@@ -33863,7 +35231,25 @@ async function main() {
33863
35231
  mode: "stateless"
33864
35232
  });
33865
35233
  const httpServer = http.createServer(async (req, res) => {
33866
- res.setHeader("Access-Control-Allow-Origin", "*");
35234
+ const requestStartedAt = Date.now();
35235
+ const requestAbortController = new AbortController();
35236
+ const abortRequest = () => requestAbortController.abort();
35237
+ req.on("aborted", abortRequest);
35238
+ req.on("close", abortRequest);
35239
+ const pathname = (() => {
35240
+ try {
35241
+ return new URL(req.url || "/", "http://localhost").pathname;
35242
+ } catch {
35243
+ return "/";
35244
+ }
35245
+ })();
35246
+ const origin = String(req.headers.origin || "").trim().toLowerCase();
35247
+ if (allowedOrigins.length > 0) {
35248
+ res.setHeader("Access-Control-Allow-Origin", allowedOrigins.includes(origin) ? origin : allowedOrigins[0]);
35249
+ res.setHeader("Vary", "Origin");
35250
+ } else {
35251
+ res.setHeader("Access-Control-Allow-Origin", "*");
35252
+ }
33867
35253
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
33868
35254
  res.setHeader(
33869
35255
  "Access-Control-Allow-Headers",
@@ -33875,17 +35261,28 @@ async function main() {
33875
35261
  res.end();
33876
35262
  return;
33877
35263
  }
33878
- const pathname = (() => {
33879
- try {
33880
- return new URL(req.url || "/", "http://localhost").pathname;
33881
- } catch {
33882
- return "/";
33883
- }
33884
- })();
35264
+ if (allowedOrigins.length > 0 && origin && !allowedOrigins.includes(origin)) {
35265
+ mcpLogger.warn("request_rejected", {
35266
+ reason: "forbidden_origin",
35267
+ method: req.method,
35268
+ route: pathname,
35269
+ status: 403
35270
+ });
35271
+ res.writeHead(403, { "Content-Type": "application/json" });
35272
+ res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32e3, message: "Forbidden origin" }, id: null }));
35273
+ return;
35274
+ }
35275
+ res.setHeader("MCP-Protocol-Version", protocolVersion);
33885
35276
  if (allowedHosts.length > 0) {
33886
35277
  const rawHost = String(req.headers.host || "").trim().toLowerCase();
33887
35278
  const normalizedHost = rawHost.startsWith("[") ? rawHost.replace(/^\[([^\]]+)\](?::\d+)?$/, "$1") : rawHost.split(":")[0];
33888
35279
  if (!allowedHosts.includes(rawHost) && !allowedHosts.includes(normalizedHost)) {
35280
+ mcpLogger.warn("request_rejected", {
35281
+ reason: "forbidden_host",
35282
+ method: req.method,
35283
+ route: pathname,
35284
+ status: 403
35285
+ });
33889
35286
  res.writeHead(403, { "Content-Type": "application/json" });
33890
35287
  res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32e3, message: "Forbidden host" }, id: null }));
33891
35288
  return;
@@ -33910,55 +35307,155 @@ async function main() {
33910
35307
  return;
33911
35308
  }
33912
35309
  if (req.method === "GET") {
35310
+ if (pathname === "/mcp") {
35311
+ res.writeHead(405, { "Content-Type": "application/json", Allow: "POST, OPTIONS" });
35312
+ res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32e3, message: "Method not allowed (stateless server offers no SSE stream)" }, id: null }));
35313
+ return;
35314
+ }
33913
35315
  res.writeHead(200, { "Content-Type": "application/json" });
33914
35316
  res.end(healthBody());
33915
35317
  return;
33916
35318
  }
33917
35319
  if (req.method !== "POST") {
33918
- res.writeHead(405, { "Content-Type": "application/json" });
35320
+ res.writeHead(405, { "Content-Type": "application/json", Allow: "POST, OPTIONS" });
33919
35321
  res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32e3, message: "Method not allowed" }, id: null }));
33920
35322
  return;
33921
35323
  }
33922
- const srv = createServer2();
33923
- const transport = new StreamableHTTPServerTransport({
33924
- sessionIdGenerator: void 0,
33925
- enableJsonResponse: true
33926
- });
33927
- let torndown = false;
33928
- const teardown = () => {
33929
- if (torndown) return;
33930
- torndown = true;
33931
- Promise.resolve(transport.close()).catch(() => {
35324
+ if (!hasSupportedAccept(String(req.headers.accept || ""))) {
35325
+ mcpLogger.warn("request_rejected", {
35326
+ reason: "unsupported_accept",
35327
+ method: req.method,
35328
+ route: pathname,
35329
+ status: 406
33932
35330
  });
33933
- Promise.resolve(srv.close()).catch(() => {
35331
+ writeJsonRpcError(
35332
+ res,
35333
+ 406,
35334
+ -32e3,
35335
+ "Not Acceptable: this endpoint supports JSON transport only"
35336
+ );
35337
+ return;
35338
+ }
35339
+ if (!isSupportedProtocolVersion(
35340
+ String(req.headers["mcp-protocol-version"] || ""),
35341
+ protocolVersions
35342
+ )) {
35343
+ mcpLogger.warn("request_rejected", {
35344
+ reason: "unsupported_protocol_version",
35345
+ method: req.method,
35346
+ route: pathname,
35347
+ status: 400,
35348
+ // The header's value can be attacker-controlled; presence is enough
35349
+ // operationally and keeps it out of the log boundary.
35350
+ protocolVersionPresent: Boolean(req.headers["mcp-protocol-version"])
33934
35351
  });
33935
- };
33936
- res.on("close", teardown);
35352
+ writeJsonRpcError(
35353
+ res,
35354
+ 400,
35355
+ -32e3,
35356
+ "Unsupported MCP protocol version"
35357
+ );
35358
+ return;
35359
+ }
35360
+ if (authToken && !isAuthorizedRequest(req, authToken, authScheme)) {
35361
+ mcpLogger.warn("request_rejected", {
35362
+ reason: "unauthorized",
35363
+ method: req.method,
35364
+ route: pathname,
35365
+ status: 401
35366
+ });
35367
+ res.setHeader("WWW-Authenticate", `${authScheme} realm="semiotic-mcp"`);
35368
+ writeJsonRpcError(res, 401, -32e3, "Unauthorized");
35369
+ return;
35370
+ }
35371
+ const requestSlot = requestLimiter.tryAcquire();
35372
+ if (!requestSlot.ok) {
35373
+ res.setHeader("Retry-After", String(Math.max(1, Math.ceil(requestSlot.retryAfterMs / 1e3))));
35374
+ mcpLogger.warn("request_rejected", {
35375
+ reason: requestSlot.code === "MCP_REQUEST_CONCURRENCY" ? "request_concurrency" : "request_rate",
35376
+ method: req.method,
35377
+ route: pathname,
35378
+ status: 429
35379
+ });
35380
+ writeJsonRpcError(res, 429, -32e3, requestSlot.message);
35381
+ return;
35382
+ }
33937
35383
  try {
33938
- await srv.connect(transport);
33939
- await transport.handleRequest(req, res);
33940
- } catch (err) {
33941
- console.error("Request handling error:", err);
33942
- if (!res.headersSent) {
33943
- res.writeHead(500, { "Content-Type": "application/json" });
33944
- res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32603, message: "Internal server error" }, id: null }));
35384
+ const bodyResult = await readJsonBodyWithLimit(req);
35385
+ if (!bodyResult.ok) {
35386
+ mcpLogger.warn("request_rejected", {
35387
+ reason: bodyResult.reason,
35388
+ method: req.method,
35389
+ route: pathname,
35390
+ status: bodyResult.status
35391
+ });
35392
+ if (!res.headersSent) {
35393
+ res.writeHead(bodyResult.status, { "Content-Type": "application/json" });
35394
+ res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: bodyResult.code, message: bodyResult.message }, id: null }));
35395
+ }
35396
+ return;
35397
+ }
35398
+ const srv = createServer2(toolProfile, {
35399
+ signal: requestAbortController.signal,
35400
+ limits: renderExecutionLimits
35401
+ });
35402
+ const transport = new StreamableHTTPServerTransport({
35403
+ sessionIdGenerator: void 0,
35404
+ enableJsonResponse: true
35405
+ });
35406
+ let torndown = false;
35407
+ const teardown = () => {
35408
+ if (torndown) return;
35409
+ torndown = true;
35410
+ Promise.resolve(transport.close()).catch(() => {
35411
+ });
35412
+ Promise.resolve(srv.close()).catch(() => {
35413
+ });
35414
+ };
35415
+ res.on("close", teardown);
35416
+ try {
35417
+ await srv.connect(transport);
35418
+ await transport.handleRequest(req, res, bodyResult.body);
35419
+ mcpLogger.info("request_completed", {
35420
+ method: req.method,
35421
+ route: pathname,
35422
+ status: res.statusCode,
35423
+ durationMs: Date.now() - requestStartedAt,
35424
+ bodyBytes: bodyResult.bodyBytes
35425
+ });
35426
+ } catch {
35427
+ mcpLogger.error("request_failed", {
35428
+ reason: "request_handler_error",
35429
+ method: req.method,
35430
+ route: pathname,
35431
+ status: 500,
35432
+ durationMs: Date.now() - requestStartedAt,
35433
+ bodyBytes: bodyResult.bodyBytes
35434
+ });
35435
+ if (!res.headersSent) {
35436
+ res.writeHead(500, { "Content-Type": "application/json" });
35437
+ res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32603, message: "Internal server error" }, id: null }));
35438
+ }
35439
+ } finally {
35440
+ teardown();
33945
35441
  }
33946
35442
  } finally {
33947
- teardown();
35443
+ requestSlot.release();
33948
35444
  }
33949
35445
  });
33950
- httpServer.listen(port, () => {
33951
- console.error(`Semiotic MCP server (HTTP) listening on http://localhost:${port}`);
33952
- console.error("Tools: getSchema, suggestChart, suggestCharts, proposeChartVariants, suggestStreamCharts, suggestDashboard, suggestStretchCharts, repairChartConfig, renderChart, renderInteractiveChart, interrogateChart, groundChart, diagnoseConfig, auditAccessibility, reportIssue, applyTheme");
33953
- console.error("Resources: semiotic://schema, semiotic://components, semiotic://behavior-contracts, semiotic://system-prompt, semiotic://examples, ui://semiotic/chart-widget.html");
35446
+ httpServer.listen(port, host, () => {
35447
+ mcpLogger.info("service_started", {
35448
+ profile: toolProfile,
35449
+ retentionDays: mcpLoggingPolicy.retentionDays
35450
+ });
33954
35451
  });
33955
35452
  } else {
33956
- const srv = createServer2();
35453
+ const srv = createServer2(toolProfile);
33957
35454
  const transport = new StdioServerTransport();
33958
35455
  await srv.connect(transport);
33959
35456
  }
33960
35457
  }
33961
- main().catch((err) => {
33962
- console.error("MCP server error:", err);
35458
+ main().catch(() => {
35459
+ mcpLogger.error("service_fatal", { reason: "service_startup_failure" });
33963
35460
  process.exit(1);
33964
35461
  });