semiotic 3.7.5 → 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.
- package/CLAUDE.md +54 -12
- package/README.md +121 -27
- package/agent-skill/semiotic-charts/SKILL.md +146 -0
- package/ai/cli.js +133 -45
- package/ai/componentMetadata.cjs +6 -1
- package/ai/dist/mcp-server.js +1651 -154
- package/ai/examples.md +174 -0
- package/ai/schema.json +2733 -536
- package/ai/surface-manifest.json +200 -0
- package/ai/system-prompt.md +18 -3
- package/dist/components/CategoryColors.d.ts +1 -1
- package/dist/components/ChartContainer.d.ts +91 -0
- package/dist/components/ChartGrid.d.ts +11 -1
- package/dist/components/CircularBrush.d.ts +61 -0
- package/dist/components/ContextLayout.d.ts +5 -1
- package/dist/components/DirectManipulationControl.d.ts +63 -0
- package/dist/components/LinkedCharts.d.ts +3 -1
- package/dist/components/MobileAnnotationCalloutList.d.ts +13 -0
- package/dist/components/MobileChartContainer.d.ts +51 -0
- package/dist/components/MobileStandardControls.d.ts +68 -0
- package/dist/components/SmallMultipleChart.d.ts +84 -0
- package/dist/components/Tooltip/FlippingTooltip.d.ts +21 -0
- package/dist/components/ai/ChartRecipe.d.ts +16 -0
- package/dist/components/ai/IntentMark.d.ts +15 -0
- package/dist/components/ai/chartCapabilities.d.ts +8 -1
- package/dist/components/ai/chartCapabilityTypes.d.ts +26 -1
- package/dist/components/ai/chartClinic.d.ts +91 -0
- package/dist/components/ai/chartRecipeRegistry.d.ts +14 -0
- package/dist/components/ai/chartRecipes.d.ts +278 -0
- package/dist/components/ai/dataPitfallsBridge.d.ts +102 -0
- package/dist/components/ai/describeChart.d.ts +7 -0
- package/dist/components/ai/describeRecipeChart.d.ts +16 -0
- package/dist/components/ai/generativeChart.d.ts +28 -3
- package/dist/components/ai/intentManifest.d.ts +62 -0
- package/dist/components/ai/navigationTree.d.ts +3 -0
- package/dist/components/ai/observedSceneAudit.d.ts +72 -0
- package/dist/components/ai/readerGrounding.d.ts +83 -0
- package/dist/components/ai/recipeCapability.d.ts +8 -0
- package/dist/components/ai/recipeNavigation.d.ts +13 -0
- package/dist/components/ai/recipeSemantics.d.ts +11 -0
- package/dist/components/ai/repairChartConfig.d.ts +9 -0
- package/dist/components/ai/suggestCharts.d.ts +11 -1
- package/dist/components/charts/custom/GeoCustomChart.d.ts +48 -0
- package/dist/components/charts/custom/NetworkCustomChart.d.ts +8 -2
- package/dist/components/charts/custom/OrdinalCustomChart.d.ts +8 -2
- package/dist/components/charts/custom/XYCustomChart.d.ts +11 -2
- package/dist/components/charts/geo/DistanceCartogram.d.ts +1 -1
- package/dist/components/charts/geo/FlowMap.d.ts +1 -1
- package/dist/components/charts/geo/ProportionalSymbolMap.d.ts +1 -1
- package/dist/components/charts/geo/index.d.ts +2 -0
- package/dist/components/charts/index.d.ts +29 -1
- package/dist/components/charts/network/ChordDiagram.d.ts +1 -1
- package/dist/components/charts/network/CirclePack.d.ts +1 -1
- package/dist/components/charts/network/ForceDirectedGraph.d.ts +15 -5
- package/dist/components/charts/network/OrbitDiagram.d.ts +1 -1
- package/dist/components/charts/network/ProcessSankey.d.ts +1 -1
- package/dist/components/charts/network/SankeyDiagram.d.ts +1 -1
- package/dist/components/charts/network/TreeDiagram.d.ts +1 -1
- package/dist/components/charts/network/Treemap.d.ts +1 -1
- package/dist/components/charts/network/useForceLayout.d.ts +17 -0
- package/dist/components/charts/ordinal/BarChart.d.ts +1 -1
- package/dist/components/charts/ordinal/BoxPlot.d.ts +1 -1
- package/dist/components/charts/ordinal/DonutChart.d.ts +1 -1
- package/dist/components/charts/ordinal/DotPlot.d.ts +1 -1
- package/dist/components/charts/ordinal/FunnelChart.d.ts +1 -1
- package/dist/components/charts/ordinal/GroupedBarChart.d.ts +1 -1
- package/dist/components/charts/ordinal/Histogram.d.ts +1 -1
- package/dist/components/charts/ordinal/PieChart.d.ts +1 -1
- package/dist/components/charts/ordinal/RidgelinePlot.d.ts +1 -1
- package/dist/components/charts/ordinal/StackedBarChart.d.ts +1 -1
- package/dist/components/charts/ordinal/SwarmPlot.d.ts +1 -1
- package/dist/components/charts/ordinal/SwimlaneChart.d.ts +1 -1
- package/dist/components/charts/ordinal/ViolinPlot.d.ts +1 -1
- package/dist/components/charts/physics/ChainReactionChart.d.ts +9 -0
- package/dist/components/charts/physics/CollisionSwarmChart.capability.d.ts +2 -0
- package/dist/components/charts/physics/CollisionSwarmChart.d.ts +47 -0
- package/dist/components/charts/physics/EventDropChart.capability.d.ts +2 -0
- package/dist/components/charts/physics/EventDropChart.d.ts +53 -0
- package/dist/components/charts/physics/GaltonBoardChart.capability.d.ts +2 -0
- package/dist/components/charts/physics/GaltonBoardChart.d.ts +66 -0
- package/dist/components/charts/physics/GauntletChart.capability.d.ts +2 -0
- package/dist/components/charts/physics/GauntletChart.d.ts +73 -0
- package/dist/components/charts/physics/PhysicalFlowChart.capability.d.ts +2 -0
- package/dist/components/charts/physics/PhysicalFlowChart.d.ts +63 -0
- package/dist/components/charts/physics/PhysicsCustomChart.d.ts +120 -0
- package/dist/components/charts/physics/PhysicsPileChart.capability.d.ts +2 -0
- package/dist/components/charts/physics/PhysicsPileChart.d.ts +53 -0
- package/dist/components/charts/physics/ProcessFlowChart.capability.d.ts +2 -0
- package/dist/components/charts/physics/ProcessFlowChart.d.ts +96 -0
- package/dist/components/charts/physics/chainReactionOverlay.d.ts +22 -0
- package/dist/components/charts/physics/chainReactionRuntime.d.ts +37 -0
- package/dist/components/charts/physics/chainReactionTypes.d.ts +97 -0
- package/dist/components/charts/physics/collisionSwarmPhysics.d.ts +27 -0
- package/dist/components/charts/physics/dependencyMachine.d.ts +188 -0
- package/dist/components/charts/physics/eventDropPhysics.d.ts +64 -0
- package/dist/components/charts/physics/galtonBoardPhysics.d.ts +28 -0
- package/dist/components/charts/physics/gauntletChartProps.d.ts +67 -0
- package/dist/components/charts/physics/gauntletChrome.d.ts +33 -0
- package/dist/components/charts/physics/gauntletController.d.ts +73 -0
- package/dist/components/charts/physics/gauntletEffects.d.ts +36 -0
- package/dist/components/charts/physics/gauntletPhysics.d.ts +71 -0
- package/dist/components/charts/physics/gauntletRuntime.d.ts +42 -0
- package/dist/components/charts/physics/gauntletTypes.d.ts +213 -0
- package/dist/components/charts/physics/physicalFlowPhysics.d.ts +57 -0
- package/dist/components/charts/physics/physicsChartShared.d.ts +41 -0
- package/dist/components/charts/physics/physicsChartUtils.d.ts +18 -0
- package/dist/components/charts/physics/physicsEncoding.d.ts +142 -0
- package/dist/components/charts/physics/physicsHocHandle.d.ts +53 -0
- package/dist/components/charts/physics/physicsHocUtils.d.ts +128 -0
- package/dist/components/charts/physics/physicsPilePhysics.d.ts +41 -0
- package/dist/components/charts/physics/physicsProcessPrimitives.d.ts +38 -0
- package/dist/components/charts/physics/processFlowPhysics.d.ts +97 -0
- package/dist/components/charts/realtime/RealtimeHeatmap.d.ts +9 -1
- package/dist/components/charts/realtime/RealtimeHistogram.d.ts +9 -1
- package/dist/components/charts/realtime/RealtimeLineChart.d.ts +9 -1
- package/dist/components/charts/realtime/RealtimeSwarmChart.d.ts +16 -2
- package/dist/components/charts/realtime/RealtimeWaterfallChart.d.ts +9 -1
- package/dist/components/charts/shared/auditMobileVisualization.d.ts +91 -0
- package/dist/components/charts/shared/chartDefinitionPilot.d.ts +145 -0
- package/dist/components/charts/shared/chartFamilySets.d.ts +20 -0
- package/dist/components/charts/shared/chartSpecCore.d.ts +165 -0
- package/dist/components/charts/shared/chartSpecs.d.ts +8 -127
- package/dist/components/charts/shared/chartSpecsGeo.d.ts +2 -0
- package/dist/components/charts/shared/chartSpecsNetwork.d.ts +2 -0
- package/dist/components/charts/shared/chartSpecsOrdinal.d.ts +2 -0
- package/dist/components/charts/shared/chartSpecsPhysics.d.ts +2 -0
- package/dist/components/charts/shared/chartSpecsRealtime.d.ts +2 -0
- package/dist/components/charts/shared/chartSpecsValue.d.ts +2 -0
- package/dist/components/charts/shared/chartSpecsXY.d.ts +2 -0
- package/dist/components/charts/shared/colorContrast.d.ts +3 -0
- package/dist/components/charts/shared/colorUtils.d.ts +16 -1
- package/dist/components/charts/shared/diagnoseAnnotationChecks.d.ts +4 -0
- package/dist/components/charts/shared/diagnoseConfig.d.ts +9 -14
- package/dist/components/charts/shared/diagnoseMisleadingChecks.d.ts +9 -0
- package/dist/components/charts/shared/diagnosePhysicsChecks.d.ts +3 -0
- package/dist/components/charts/shared/diagnoseTypes.d.ts +10 -0
- package/dist/components/charts/shared/hooks.d.ts +28 -5
- package/dist/components/charts/shared/motionEncoding.d.ts +145 -0
- package/dist/components/charts/shared/responsiveRules.d.ts +56 -0
- package/dist/components/charts/shared/streamPropsHelpers.d.ts +12 -2
- package/dist/components/charts/shared/temporalStrings.d.ts +7 -0
- package/dist/components/charts/shared/types.d.ts +49 -1
- package/dist/components/charts/shared/useChartSetup.d.ts +9 -2
- package/dist/components/charts/shared/useCustomChartSetup.d.ts +14 -5
- package/dist/components/charts/shared/useNetworkChartSetup.d.ts +6 -2
- package/dist/components/charts/shared/useOrdinalPieceStyle.d.ts +1 -1
- package/dist/components/charts/shared/useStreamingLegend.d.ts +1 -1
- package/dist/components/charts/shared/useSyncedPushData.d.ts +49 -0
- package/dist/components/charts/shared/validationMap.d.ts +0 -11
- package/dist/components/charts/xy/AreaChart.d.ts +1 -1
- package/dist/components/charts/xy/BubbleChart.d.ts +1 -1
- package/dist/components/charts/xy/LineChart.d.ts +1 -1
- package/dist/components/charts/xy/MinimapChart.d.ts +1 -1
- package/dist/components/charts/xy/MultiAxisLineChart.d.ts +2 -2
- package/dist/components/charts/xy/QuadrantChart.d.ts +1 -1
- package/dist/components/charts/xy/Scatterplot.d.ts +1 -1
- package/dist/components/charts/xy/ScatterplotMatrix.d.ts +11 -1
- package/dist/components/charts/xy/StackedAreaChart.d.ts +9 -4
- package/dist/components/controls/controlAudit.d.ts +24 -0
- package/dist/components/controls/controlContract.d.ts +57 -0
- package/dist/components/data/fromFlintChart.d.ts +74 -0
- package/dist/components/data/fromVegaLite.d.ts +27 -0
- package/dist/components/data/portability/index.d.ts +6 -3
- package/dist/components/data/portability/result.d.ts +52 -0
- package/dist/components/data/portability/spec.d.ts +31 -0
- package/dist/components/data/portability/vegaLite.d.ts +24 -6
- package/dist/components/export/chartConfig.d.ts +11 -0
- package/dist/components/realtime/types.d.ts +11 -0
- package/dist/components/recipes/annotationLayout.d.ts +29 -0
- package/dist/components/recipes/axisFixedForce.d.ts +119 -0
- package/dist/components/recipes/{gofishBoba.d.ts → boba.d.ts} +2 -2
- package/dist/components/recipes/cyclical.d.ts +39 -0
- package/dist/components/recipes/edgeRouter.d.ts +103 -0
- package/dist/components/recipes/forceLayout.d.ts +48 -0
- package/dist/components/recipes/forceLayoutAsync.d.ts +17 -0
- package/dist/components/recipes/gofishBobaHandwritten.d.ts +3 -0
- package/dist/components/recipes/gofishDisplayListFixtures.generated.d.ts +37 -0
- package/dist/components/recipes/gofishIR.d.ts +178 -132
- package/dist/components/recipes/gofishIRExamples.d.ts +30 -81
- package/dist/components/recipes/intervalLanes.d.ts +87 -0
- package/dist/components/recipes/intervals.d.ts +80 -0
- package/dist/components/recipes/isometricLandmarks.d.ts +88 -0
- package/dist/components/recipes/isotypeGlyphs.d.ts +17 -0
- package/dist/components/recipes/mobileAnnotationStrategy.d.ts +48 -0
- package/dist/components/recipes/mobileChartFamilyRecipes.d.ts +59 -0
- package/dist/components/recipes/networkAnalysis.d.ts +102 -0
- package/dist/components/recipes/networkLayouts.d.ts +83 -0
- package/dist/components/recipes/physics.d.ts +122 -0
- package/dist/components/recipes/physicsReference.d.ts +102 -0
- package/dist/components/recipes/processAggregates.d.ts +32 -0
- package/dist/components/recipes/processChrome.d.ts +67 -0
- package/dist/components/recipes/processJourney.d.ts +54 -0
- package/dist/components/recipes/processPhysics.d.ts +300 -0
- package/dist/components/recipes/processVolumeGeometry.d.ts +28 -0
- package/dist/components/recipes/radialCoords.d.ts +69 -0
- package/dist/components/recipes/random.d.ts +2 -0
- package/dist/components/recipes/recipeChrome.d.ts +91 -0
- package/dist/components/recipes/recipeGlyph.d.ts +42 -0
- package/dist/components/recipes/recipeLegend.d.ts +53 -1
- package/dist/components/recipes/recipeUtils.d.ts +52 -0
- package/dist/components/recipes/runs.d.ts +59 -0
- package/dist/components/recipes/tokenEncoding.d.ts +157 -0
- package/dist/components/recipes/tokenLayer.d.ts +79 -0
- package/dist/components/recipes/unitize.d.ts +108 -0
- package/dist/components/recipes/vector.d.ts +28 -0
- package/dist/components/recipes/waffle.d.ts +43 -0
- package/dist/components/semiotic-ai-core.d.ts +47 -0
- package/dist/components/semiotic-ai.d.ts +50 -8
- package/dist/components/semiotic-controls.d.ts +18 -0
- package/dist/components/semiotic-experimental.d.ts +38 -9
- package/dist/components/semiotic-geo.d.ts +11 -0
- package/dist/components/semiotic-network.d.ts +11 -0
- package/dist/components/semiotic-ordinal.d.ts +8 -0
- package/dist/components/semiotic-physics-matter.d.ts +10 -0
- package/dist/components/semiotic-physics-rapier.d.ts +9 -0
- package/dist/components/semiotic-physics.d.ts +56 -0
- package/dist/components/semiotic-realtime-core.d.ts +34 -0
- package/dist/components/semiotic-realtime-react.d.ts +10 -0
- package/dist/components/semiotic-realtime.d.ts +4 -30
- package/dist/components/semiotic-recipes-core.d.ts +92 -0
- package/dist/components/semiotic-recipes-react.d.ts +7 -0
- package/dist/components/semiotic-recipes.d.ts +4 -43
- package/dist/components/semiotic-server-edge.d.ts +8 -0
- package/dist/components/semiotic-server-node.d.ts +5 -0
- package/dist/components/semiotic-server.d.ts +2 -2
- package/dist/components/semiotic-themes-core.d.ts +53 -0
- package/dist/components/semiotic-themes-react.d.ts +4 -0
- package/dist/components/semiotic-themes.d.ts +6 -76
- package/dist/components/semiotic-utils-core.d.ts +46 -0
- package/dist/components/semiotic-utils-react.d.ts +5 -0
- package/dist/components/semiotic-utils.d.ts +3 -39
- package/dist/components/semiotic-xy.d.ts +8 -0
- package/dist/components/semiotic.d.ts +35 -7
- package/dist/components/server/animatedGif.d.ts +36 -0
- package/dist/components/server/renderEvidence.d.ts +1 -1
- package/dist/components/server/renderToStaticSVG.d.ts +3 -35
- package/dist/components/server/serverChartConfigShared.d.ts +17 -0
- package/dist/components/server/serverChartConfigs.d.ts +19 -9
- package/dist/components/server/serverChartConfigsCustom.d.ts +5 -0
- package/dist/components/server/serverChartConfigsGeo.d.ts +9 -0
- package/dist/components/server/serverChartConfigsNetwork.d.ts +8 -0
- package/dist/components/server/serverChartConfigsOrdinal.d.ts +16 -0
- package/dist/components/server/serverChartConfigsPhysics.d.ts +9 -0
- package/dist/components/server/serverChartConfigsXY.d.ts +11 -0
- package/dist/components/server/staticGeo.d.ts +4 -0
- package/dist/components/server/staticLegend.d.ts +1 -1
- package/dist/components/server/staticNetwork.d.ts +8 -0
- package/dist/components/server/staticOrdinal.d.ts +11 -0
- package/dist/components/server/staticPhysics.d.ts +3 -0
- package/dist/components/server/staticSVGChrome.d.ts +110 -0
- package/dist/components/server/staticXY.d.ts +4 -0
- package/dist/components/store/ObservationStore.d.ts +3 -2
- package/dist/components/store/themeSerialization.d.ts +32 -0
- package/dist/components/store/useChartInterrogation.d.ts +9 -0
- package/dist/components/stream/AccessibleDataTable.d.ts +3 -13
- package/dist/components/stream/FocusRing.d.ts +4 -2
- package/dist/components/stream/GeoCanvasHitTester.d.ts +1 -1
- package/dist/components/stream/GeoPipelineStore.d.ts +28 -0
- package/dist/components/stream/NetworkPipelineStore.d.ts +31 -26
- package/dist/components/stream/NetworkSVGOverlay.d.ts +4 -1
- package/dist/components/stream/OrdinalPipelineStore.d.ts +46 -2
- package/dist/components/stream/PipelineStore.d.ts +81 -170
- package/dist/components/stream/SceneGraph.d.ts +9 -1
- package/dist/components/stream/StreamGeoFrame.d.ts +1 -1
- package/dist/components/stream/StreamNetworkFrame.d.ts +1 -1
- package/dist/components/stream/StreamOrdinalFrame.d.ts +1 -1
- package/dist/components/stream/StreamXYFrame.d.ts +2 -18
- package/dist/components/stream/accessibleDataRows.d.ts +33 -0
- package/dist/components/stream/accessorUtils.d.ts +23 -10
- package/dist/components/stream/brushAccessibility.d.ts +25 -0
- package/dist/components/stream/canvasBackground.d.ts +24 -0
- package/dist/components/stream/canvasSetup.d.ts +4 -1
- package/dist/components/stream/customLayoutFailure.d.ts +33 -0
- package/dist/components/stream/customLayoutPalette.d.ts +19 -9
- package/dist/components/stream/frameThemeColors.d.ts +24 -0
- package/dist/components/stream/geoAnnotationAnchors.d.ts +14 -0
- package/dist/components/stream/geoCartogram.d.ts +17 -0
- package/dist/components/stream/geoCustomLayout.d.ts +59 -0
- package/dist/components/stream/geoDefaultTooltip.d.ts +11 -0
- package/dist/components/stream/geoFrameHelpers.d.ts +34 -0
- package/dist/components/stream/geoPipelineHelpers.d.ts +46 -0
- package/dist/components/stream/geoTypes.d.ts +56 -7
- package/dist/components/stream/glyphDef.d.ts +98 -0
- package/dist/components/stream/hitTarget.d.ts +170 -0
- package/dist/components/stream/hoverUtils.d.ts +3 -0
- package/dist/components/stream/keyboardNav.d.ts +4 -1
- package/dist/components/stream/layouts/forceLayoutPlugin.d.ts +9 -1
- package/dist/components/stream/layouts/forceLayoutWorkerClient.d.ts +57 -0
- package/dist/components/stream/networkBezier.d.ts +10 -0
- package/dist/components/stream/networkColorAccessors.d.ts +49 -0
- package/dist/components/stream/networkDefaultTooltip.d.ts +10 -0
- package/dist/components/stream/networkFrameInteraction.d.ts +37 -0
- package/dist/components/stream/networkFramePaint.d.ts +43 -0
- package/dist/components/stream/networkPipelineConfig.d.ts +22 -0
- package/dist/components/stream/networkPipelineHelpers.d.ts +12 -0
- package/dist/components/stream/networkRealtimeEncoding.d.ts +41 -0
- package/dist/components/stream/networkTypes.d.ts +76 -5
- package/dist/components/stream/ordinalCanvasRenderers.d.ts +7 -0
- package/dist/components/stream/ordinalDataIndex.d.ts +5 -0
- package/dist/components/stream/ordinalDefaultTooltip.d.ts +10 -0
- package/dist/components/stream/ordinalDomain.d.ts +38 -0
- package/dist/components/stream/ordinalPipelineUpdateResults.d.ts +12 -0
- package/dist/components/stream/ordinalPulse.d.ts +10 -0
- package/dist/components/stream/ordinalPulseResources.d.ts +3 -0
- package/dist/components/stream/ordinalSceneBuilders/sceneBuilderMap.d.ts +5 -0
- package/dist/components/stream/ordinalSpatialIndex.d.ts +9 -0
- package/dist/components/stream/ordinalTypes.d.ts +40 -8
- package/dist/components/stream/paintNeeds.d.ts +31 -0
- package/dist/components/stream/physics/CapacityQueueController.d.ts +5 -0
- package/dist/components/stream/physics/CapacityQueueTypes.d.ts +134 -0
- package/dist/components/stream/physics/MatterPhysicsEngineAdapter.d.ts +42 -0
- package/dist/components/stream/physics/PhysicsAccessibility.d.ts +48 -0
- package/dist/components/stream/physics/PhysicsAnnotations.d.ts +58 -0
- package/dist/components/stream/physics/PhysicsBodyBudget.d.ts +26 -0
- package/dist/components/stream/physics/PhysicsBodySpatialIndex.d.ts +10 -0
- package/dist/components/stream/physics/PhysicsCanvasTheme.d.ts +26 -0
- package/dist/components/stream/physics/PhysicsControllers.d.ts +75 -0
- package/dist/components/stream/physics/PhysicsEngineAdapter.d.ts +59 -0
- package/dist/components/stream/physics/PhysicsEngineConformance.d.ts +23 -0
- package/dist/components/stream/physics/PhysicsEvidence.d.ts +25 -0
- package/dist/components/stream/physics/PhysicsKernel.d.ts +207 -0
- package/dist/components/stream/physics/PhysicsOptionalEngineAdapters.d.ts +12 -0
- package/dist/components/stream/physics/PhysicsPipelineStore.d.ts +84 -0
- package/dist/components/stream/physics/PhysicsPipelineTypes.d.ts +181 -0
- package/dist/components/stream/physics/PhysicsSVGOverlay.d.ts +72 -0
- package/dist/components/stream/physics/PhysicsSediment.d.ts +69 -0
- package/dist/components/stream/physics/PhysicsSettledSVG.d.ts +18 -0
- package/dist/components/stream/physics/PhysicsSettledScene.d.ts +21 -0
- package/dist/components/stream/physics/PhysicsWorkerClient.d.ts +24 -0
- package/dist/components/stream/physics/PhysicsWorkerProtocol.d.ts +101 -0
- package/dist/components/stream/physics/PhysicsWorkerRuntime.d.ts +5 -0
- package/dist/components/stream/physics/RapierPhysicsEngineAdapter.d.ts +18 -0
- package/dist/components/stream/physics/ServiceOperationsControllers.d.ts +27 -0
- package/dist/components/stream/physics/ServiceOperationsTypes.d.ts +89 -0
- package/dist/components/stream/physics/StreamPhysicsFrame.d.ts +6 -0
- package/dist/components/stream/physics/StreamPhysicsTypes.d.ts +302 -0
- package/dist/components/stream/physics/physicsBodyCanvas.d.ts +27 -0
- package/dist/components/stream/physics/physicsPipelineControls.d.ts +4 -0
- package/dist/components/stream/physics/physicsPipelineHelpers.d.ts +41 -0
- package/dist/components/stream/physics/physicsPipelineObservations.d.ts +14 -0
- package/dist/components/stream/physics/physicsPipelineUpdateResults.d.ts +9 -0
- package/dist/components/stream/physics/physicsRegionRuntime.d.ts +54 -0
- package/dist/components/stream/physics/physicsSemanticUI.d.ts +20 -0
- package/dist/components/stream/physics/usePhysicsFrameLifecyclePolicy.d.ts +16 -0
- package/dist/components/stream/pipelineBufferUtils.d.ts +26 -0
- package/dist/components/stream/pipelineConfig.d.ts +177 -0
- package/dist/components/stream/pipelineDecay.d.ts +20 -2
- package/dist/components/stream/pipelineDomainResolution.d.ts +62 -0
- package/dist/components/stream/pipelineIdentityOps.d.ts +26 -0
- package/dist/components/stream/pipelinePulse.d.ts +16 -2
- package/dist/components/stream/pipelineRibbons.d.ts +41 -0
- package/dist/components/stream/pipelineSpatialIndex.d.ts +12 -0
- package/dist/components/stream/pipelineStoreUpdateResults.d.ts +12 -0
- package/dist/components/stream/pipelineStyleResolvers.d.ts +46 -0
- package/dist/components/stream/pipelineTransitions.d.ts +2 -0
- package/dist/components/stream/pipelineUpdateContract.d.ts +62 -0
- package/dist/components/stream/pulseFrameRefresh.d.ts +22 -0
- package/dist/components/stream/renderers/glyphCanvasRenderer.d.ts +6 -0
- package/dist/components/stream/renderers/resolveCSSColor.d.ts +6 -0
- package/dist/components/stream/sceneRevisionDiagnostics.d.ts +18 -0
- package/dist/components/stream/streamStoreSync.d.ts +37 -0
- package/dist/components/stream/types.d.ts +135 -7
- package/dist/components/stream/useFrame.d.ts +21 -2
- package/dist/components/stream/useHydration.d.ts +6 -0
- package/dist/components/stream/useLegendCategoryEmission.d.ts +5 -0
- package/dist/components/stream/workerModuleUrl.d.ts +10 -0
- package/dist/components/stream/xyAnnotationAnchors.d.ts +13 -0
- package/dist/components/stream/xyCanvasRenderers.d.ts +7 -0
- package/dist/components/stream/xyCrosshair.d.ts +11 -0
- package/dist/components/stream/xyDateTicks.d.ts +9 -0
- package/dist/components/stream/xyDefaultTooltip.d.ts +10 -0
- package/dist/components/stream/xySceneBuilders/types.d.ts +4 -4
- package/dist/controls.min.js +2 -0
- package/dist/controls.module.min.js +2 -0
- package/dist/forceLayoutWorker.js +1 -0
- package/dist/geo.min.js +1 -1
- package/dist/geo.module.min.js +1 -1
- package/dist/network.min.js +1 -1
- package/dist/network.module.min.js +1 -1
- package/dist/ordinal.min.js +1 -1
- package/dist/ordinal.module.min.js +1 -1
- package/dist/physics-matter.min.js +1 -0
- package/dist/physics-matter.module.min.js +1 -0
- package/dist/physics-rapier.min.js +1 -0
- package/dist/physics-rapier.module.min.js +1 -0
- package/dist/physics.min.js +2 -0
- package/dist/physics.module.min.js +2 -0
- package/dist/physicsWorker.js +1 -0
- package/dist/realtime.min.js +1 -1
- package/dist/realtime.module.min.js +1 -1
- package/dist/semiotic-ai-chunk-OG7BQUUW.module.min.js +2 -0
- package/dist/semiotic-ai-core.d.ts +47 -0
- package/dist/semiotic-ai-core.min.js +1 -0
- package/dist/semiotic-ai-core.module.min.js +1 -0
- package/dist/semiotic-ai-statisticalOverlays-CY4WKR4A.module.min.js +2 -0
- package/dist/semiotic-ai.d.ts +50 -8
- package/dist/semiotic-ai.min.js +1 -1
- package/dist/semiotic-ai.module.min.js +1 -1
- package/dist/semiotic-chunk-3FSVZ46U.module.min.js +2 -0
- package/dist/semiotic-controls.d.ts +18 -0
- package/dist/semiotic-data.min.js +1 -1
- package/dist/semiotic-data.module.min.js +1 -1
- package/dist/semiotic-experimental.d.ts +38 -9
- package/dist/semiotic-experimental.min.js +1 -1
- package/dist/semiotic-experimental.module.min.js +1 -1
- package/dist/semiotic-geo.d.ts +11 -0
- package/dist/semiotic-network.d.ts +11 -0
- package/dist/semiotic-ordinal.d.ts +8 -0
- package/dist/semiotic-physics-matter.d.ts +10 -0
- package/dist/semiotic-physics-rapier.d.ts +9 -0
- package/dist/semiotic-physics.d.ts +56 -0
- package/dist/semiotic-realtime-core.d.ts +34 -0
- package/dist/semiotic-realtime-core.min.js +1 -0
- package/dist/semiotic-realtime-core.module.min.js +1 -0
- package/dist/semiotic-realtime-react.d.ts +10 -0
- package/dist/semiotic-realtime-react.min.js +2 -0
- package/dist/semiotic-realtime-react.module.min.js +2 -0
- package/dist/semiotic-realtime.d.ts +4 -30
- package/dist/semiotic-recipes-core.d.ts +92 -0
- package/dist/semiotic-recipes-core.min.js +1 -0
- package/dist/semiotic-recipes-core.module.min.js +1 -0
- package/dist/semiotic-recipes-react.d.ts +7 -0
- package/dist/semiotic-recipes-react.min.js +2 -0
- package/dist/semiotic-recipes-react.module.min.js +2 -0
- package/dist/semiotic-recipes.d.ts +4 -43
- package/dist/semiotic-recipes.min.js +1 -1
- package/dist/semiotic-recipes.module.min.js +1 -1
- package/dist/semiotic-server-edge.d.ts +8 -0
- package/dist/semiotic-server-edge.min.js +1 -0
- package/dist/semiotic-server-edge.module.min.js +1 -0
- package/dist/semiotic-server-node.d.ts +5 -0
- package/dist/semiotic-server-node.min.js +1 -0
- package/dist/semiotic-server-node.module.min.js +1 -0
- package/dist/semiotic-server.d.ts +2 -2
- package/dist/semiotic-statisticalOverlays-UOMSFKVJ.module.min.js +2 -0
- package/dist/semiotic-themes-core.d.ts +53 -0
- package/dist/semiotic-themes-core.min.js +1 -0
- package/dist/semiotic-themes-core.module.min.js +1 -0
- package/dist/semiotic-themes-react.d.ts +4 -0
- package/dist/semiotic-themes-react.min.js +2 -0
- package/dist/semiotic-themes-react.module.min.js +2 -0
- package/dist/semiotic-themes.d.ts +6 -76
- package/dist/semiotic-themes.min.js +1 -2
- package/dist/semiotic-themes.module.min.js +1 -2
- package/dist/semiotic-utils-core.d.ts +46 -0
- package/dist/semiotic-utils-core.min.js +1 -0
- package/dist/semiotic-utils-core.module.min.js +1 -0
- package/dist/semiotic-utils-react.d.ts +5 -0
- package/dist/semiotic-utils-react.min.js +2 -0
- package/dist/semiotic-utils-react.module.min.js +2 -0
- package/dist/semiotic-utils.d.ts +3 -39
- package/dist/semiotic-utils.min.js +1 -2
- package/dist/semiotic-utils.module.min.js +1 -2
- package/dist/semiotic-value.min.js +1 -1
- package/dist/semiotic-value.module.min.js +1 -1
- package/dist/semiotic-xy.d.ts +8 -0
- package/dist/semiotic.d.ts +35 -7
- package/dist/semiotic.min.js +1 -1
- package/dist/semiotic.module.min.js +1 -1
- package/dist/server.min.js +1 -1
- package/dist/server.module.min.js +1 -1
- package/dist/xy-chunk-IWD6IB6V.module.min.js +2 -0
- package/dist/xy-statisticalOverlays-7RWG6LJT.module.min.js +2 -0
- package/dist/xy.min.js +1 -1
- package/dist/xy.module.min.js +1 -1
- package/package.json +239 -87
- package/spec/README.md +152 -0
- package/spec/bindings/README.md +35 -0
- package/spec/bindings/vega-lite.mjs +112 -0
- package/spec/v0.1/annotation-provenance.schema.json +127 -0
- package/spec/v0.1/audience-profile.schema.json +67 -0
- package/spec/v0.1/chart-capability.schema.json +186 -0
- package/dist/components/recipes/gofish.d.ts +0 -207
- package/dist/components/recipes/gofishInterpreter.d.ts +0 -52
- package/dist/components/recipes/gofishLambdas.d.ts +0 -145
- package/dist/semiotic-ai-semiotic-ai-u954ylUn.js +0 -2
- package/dist/semiotic-ai-statisticalOverlays-CU7jW05Q.js +0 -1
- package/dist/semiotic-semiotic-BmrYbi99.js +0 -2
- package/dist/semiotic-statisticalOverlays-IjZw8Lsu.js +0 -1
- package/dist/xy-semiotic-xy-Dufu3D0-.js +0 -2
- package/dist/xy-statisticalOverlays-3Ni9bRph.js +0 -1
package/ai/dist/mcp-server.js
CHANGED
|
@@ -3206,11 +3206,11 @@ var require_utils = __commonJS({
|
|
|
3206
3206
|
output.address = address.join("");
|
|
3207
3207
|
return output;
|
|
3208
3208
|
}
|
|
3209
|
-
function normalizeIPv6(
|
|
3210
|
-
if (findToken(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
3388
|
-
if (!isIPv4(
|
|
3389
|
-
const ipV6res = normalizeIPv6(
|
|
3387
|
+
let host2 = unescape(component.host);
|
|
3388
|
+
if (!isIPv4(host2)) {
|
|
3389
|
+
const ipV6res = normalizeIPv6(host2);
|
|
3390
3390
|
if (ipV6res.isIPV6 === true) {
|
|
3391
|
-
|
|
3391
|
+
host2 = `[${ipV6res.escapedHost}]`;
|
|
3392
3392
|
} else {
|
|
3393
|
-
|
|
3393
|
+
host2 = reescapeHostDelimiters(host2, false);
|
|
3394
3394
|
}
|
|
3395
3395
|
}
|
|
3396
|
-
uriTokens.push(
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
31208
|
-
if (!
|
|
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}://${
|
|
31221
|
-
if (url2.hostname.length !==
|
|
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
|
-
|
|
32530
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
32708
|
-
const
|
|
32709
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
32738
|
-
dataDrawer.innerHTML = renderTable(
|
|
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 } =
|
|
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
|
-
|
|
32910
|
-
|
|
32911
|
-
|
|
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
|
|
32917
|
-
|
|
32918
|
-
|
|
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: "
|
|
33871
|
+
{ type: "image", data: base643, mimeType: "image/png" },
|
|
32923
33872
|
...evidenceBlock ? [evidenceBlock] : []
|
|
32924
33873
|
]
|
|
32925
|
-
};
|
|
33874
|
+
});
|
|
32926
33875
|
} catch (err) {
|
|
32927
|
-
if (err
|
|
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: ${
|
|
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
|
|
32964
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
33718
|
-
|
|
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.
|
|
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
|
-
|
|
33786
|
-
|
|
33787
|
-
|
|
33788
|
-
|
|
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.
|
|
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
|
-
|
|
33825
|
-
|
|
33826
|
-
|
|
33827
|
-
|
|
33828
|
-
|
|
33829
|
-
|
|
33830
|
-
|
|
33831
|
-
|
|
33832
|
-
|
|
33833
|
-
external_exports3.string(),
|
|
33834
|
-
external_exports3.
|
|
33835
|
-
|
|
33836
|
-
|
|
33837
|
-
|
|
33838
|
-
|
|
33839
|
-
|
|
33840
|
-
|
|
33841
|
-
|
|
33842
|
-
|
|
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
|
-
|
|
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
|
-
|
|
33879
|
-
|
|
33880
|
-
|
|
33881
|
-
|
|
33882
|
-
|
|
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
|
-
|
|
33923
|
-
|
|
33924
|
-
|
|
33925
|
-
|
|
33926
|
-
|
|
33927
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
33939
|
-
|
|
33940
|
-
|
|
33941
|
-
|
|
33942
|
-
|
|
33943
|
-
|
|
33944
|
-
|
|
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
|
-
|
|
35443
|
+
requestSlot.release();
|
|
33948
35444
|
}
|
|
33949
35445
|
});
|
|
33950
|
-
httpServer.listen(port, () => {
|
|
33951
|
-
|
|
33952
|
-
|
|
33953
|
-
|
|
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((
|
|
33962
|
-
|
|
35458
|
+
main().catch(() => {
|
|
35459
|
+
mcpLogger.error("service_fatal", { reason: "service_startup_failure" });
|
|
33963
35460
|
process.exit(1);
|
|
33964
35461
|
});
|