diff --git a/src/DynamoCore/Graph/Nodes/IValueSchemaProvider.cs b/src/DynamoCore/Graph/Nodes/IValueSchemaProvider.cs
new file mode 100644
index 00000000000..c92df8e4e25
--- /dev/null
+++ b/src/DynamoCore/Graph/Nodes/IValueSchemaProvider.cs
@@ -0,0 +1,22 @@
+namespace Dynamo.Graph.Nodes
+{
+ ///
+ /// Implemented by NodeModels that can describe their value type
+ /// for external consumers (DynamoPlayer, MCP, etc.).
+ /// Returns a canonical Forge Data Schema type identifier and list context.
+ ///
+ public interface IValueSchemaProvider
+ {
+ ///
+ /// Canonical type identifier in Forge Data Schema format.
+ /// Examples: "autodesk.math:point3d-1.0.0", "String", "Float64".
+ /// Returns null if the type is unknown or not yet determined.
+ ///
+ string ValueTypeId { get; }
+
+ ///
+ /// Whether the value is a list (array) of the declared type.
+ ///
+ bool IsListValue { get; }
+ }
+}
diff --git a/src/DynamoCore/PublicAPI.Unshipped.txt b/src/DynamoCore/PublicAPI.Unshipped.txt
index 475a38b3c81..fd19c94b62b 100644
--- a/src/DynamoCore/PublicAPI.Unshipped.txt
+++ b/src/DynamoCore/PublicAPI.Unshipped.txt
@@ -1,3 +1,6 @@
+Dynamo.Graph.Nodes.IValueSchemaProvider
+Dynamo.Graph.Nodes.IValueSchemaProvider.IsListValue.get -> bool
+Dynamo.Graph.Nodes.IValueSchemaProvider.ValueTypeId.get -> string
Dynamo.Models.DynamoModel.DefaultStartConfiguration.EnableUnTrustedLocationsNotifications.get -> bool
Dynamo.Models.DynamoModel.DefaultStartConfiguration.EnableUnTrustedLocationsNotifications.set -> void
Dynamo.Models.DynamoModel.IStartConfiguration.EnableUnTrustedLocationsNotifications.get -> bool
diff --git a/src/Libraries/CoreNodeModels/DefineData.cs b/src/Libraries/CoreNodeModels/DefineData.cs
index 1703a43f901..375bf6b7f7c 100644
--- a/src/Libraries/CoreNodeModels/DefineData.cs
+++ b/src/Libraries/CoreNodeModels/DefineData.cs
@@ -25,8 +25,25 @@ namespace CoreNodeModels
[OutPortDescriptions(typeof(Properties.Resources), nameof(Properties.Resources.DefineDataOutputTooltip))]
[IsDesignScriptCompatible]
[AlsoKnownAs("Data.DefineData")]
- public class DefineData : DSDropDownBase
+ public class DefineData : DSDropDownBase, IValueSchemaProvider
{
+ ///
+ [JsonIgnore]
+ public string ValueTypeId
+ {
+ get
+ {
+ if (SelectedIndex < 0 || SelectedIndex >= Items.Count)
+ return SelectedString;
+
+ return (Items[SelectedIndex].Item as Data.DataNodeDynamoType)?.TypeId ?? SelectedString;
+ }
+ }
+
+ ///
+ [JsonIgnore]
+ public bool IsListValue => IsList;
+
private bool isAutoMode = true; // default start with auto-detect 'on'
private bool isList;
private string displayValue = Properties.Resources.DefineDataDisplayValueMessage;
diff --git a/src/Libraries/CoreNodes/Data.cs b/src/Libraries/CoreNodes/Data.cs
index 959586acacc..d28f2e098ac 100644
--- a/src/Libraries/CoreNodes/Data.cs
+++ b/src/Libraries/CoreNodes/Data.cs
@@ -534,7 +534,7 @@ public static Dictionary Remember([ArbitraryDimensionArrayImport
///
/// A class representing a DataType supported by Dynamo
///
- internal class DataNodeDynamoType(Type type, string name = null)
+ internal class DataNodeDynamoType(Type type, string name = null, string typeId = null)
{
///
/// The underlying Type
@@ -545,6 +545,17 @@ internal class DataNodeDynamoType(Type type, string name = null)
///
public string Name { get; private set; } = name ?? type.Name;
///
+ /// Wire-format $typeid as emitted by ProtoGeometry's ToJson() / DSCore.Data.StringifyJSON
+ /// (e.g. "autodesk.math:point3d-1.0.0", "autodesk.geometry.curve:bcurve-1.0.0").
+ /// Null for primitive types (bool, string, Number, etc.) that don't use $typeid serialization.
+ /// Must match the identifiers recognised by
+ /// and the actual $typeid values produced by ProtoGeometry serialization.
+ /// Exposed to external consumers via
+ /// (implemented by DefineData), which DynamoPlayer reads to populate
+ /// ValueSchema.TypeId and DynamoMCP uses to resolve JSON Schemas for LLM inputs.
+ ///
+ public string TypeId { get; private set; } = typeId;
+ ///
/// The hierarchical level to be displayed in the UI
///
public int Level { get; private set; } = 0;
@@ -557,8 +568,8 @@ internal class DataNodeDynamoType(Type type, string name = null)
///
public DataNodeDynamoType Parent { get; private set; }
- public DataNodeDynamoType(Type type, int level, bool isLastChild = false, string name = null, DataNodeDynamoType parent = null)
- : this(type, name)
+ public DataNodeDynamoType(Type type, int level, bool isLastChild = false, string name = null, DataNodeDynamoType parent = null, string typeId = null)
+ : this(type, name, typeId)
{
Level = level;
IsLastChild = isLastChild;
@@ -577,41 +588,41 @@ public DataNodeDynamoType(Type type, int level, bool isLastChild = false, string
///
static Data()
{
- var curve = new DataNodeDynamoType(typeof(Curve), 0, false, null, null);
- var polyCurve = new DataNodeDynamoType(typeof(PolyCurve), 1, false, null, curve);
- var polygon = new DataNodeDynamoType(typeof(Polygon), 2, false, null, polyCurve); // polygon is subtype of polyCurve
- var rectangle = new DataNodeDynamoType(typeof(Autodesk.DesignScript.Geometry.Rectangle), 3, true, null, polyCurve); // rectangle is subtype of polygon
- var solid = new DataNodeDynamoType(typeof(Solid), 0, false, null, null);
- var cone = new DataNodeDynamoType(typeof(Cone), 1, false, null, solid); // cone is subtype of solid
- var cylinder = new DataNodeDynamoType(typeof(Cylinder), 2, false, null, cone); // cylinder is subtype of cone
- var cuboid = new DataNodeDynamoType(typeof(Cuboid), 1, false, null, solid); // cuboid is subtype of solid
- var sphere = new DataNodeDynamoType(typeof(Sphere), 1, true, null, solid); // sphere is subtype of solid
-
- var surface = new DataNodeDynamoType(typeof(Surface), 0, false, null, null);
+ var curve = new DataNodeDynamoType(typeof(Curve), 0, false, null, null, "dynamo.geometry:sab-1.0.0");
+ var polyCurve = new DataNodeDynamoType(typeof(PolyCurve), 1, false, null, curve, "autodesk.geometry.curve:compositecurve-1.0.0");
+ var polygon = new DataNodeDynamoType(typeof(Polygon), 2, false, null, polyCurve, "autodesk.geometry.curve:polyline-1.0.0");
+ var rectangle = new DataNodeDynamoType(typeof(Autodesk.DesignScript.Geometry.Rectangle), 3, true, null, polyCurve, "dynamo.geometry:rectangle-1.0.0");
+ var solid = new DataNodeDynamoType(typeof(Solid), 0, false, null, null, "dynamo.geometry:sab-1.0.0");
+ var cone = new DataNodeDynamoType(typeof(Cone), 1, false, null, solid, "dynamo.geometry:cone-1.0.0");
+ var cylinder = new DataNodeDynamoType(typeof(Cylinder), 2, false, null, cone, "autodesk.geometry.surface:cylinder-2.0.0");
+ var cuboid = new DataNodeDynamoType(typeof(Cuboid), 1, false, null, solid, "dynamo.geometry:cuboid-1.0.0");
+ var sphere = new DataNodeDynamoType(typeof(Sphere), 1, true, null, solid, "autodesk.geometry.surface:sphere-1.0.0");
+
+ var surface = new DataNodeDynamoType(typeof(Surface), 0, false, null, null, "dynamo.geometry:sab-1.0.0");
var typeList = new List
{
new(typeof(bool)),
- new(typeof(BoundingBox)),
- new(typeof(CoordinateSystem)),
+ new(typeof(BoundingBox), typeId: "autodesk.geometry:boundingbox3d-1.0.0"),
+ new(typeof(CoordinateSystem), typeId: "autodesk.math:matrix44d-1.0.0"),
curve,
- new(typeof(Arc), 1, false, null, curve),
- new(typeof(Circle), 1, false, null, curve),
- new(typeof(Ellipse), 1, false, null, curve),
- new(typeof(EllipseArc), 1, false, null, curve),
- new(typeof(Helix), 1, false, null, curve),
- new(typeof(Line), 1, false, null, curve),
- new(typeof(NurbsCurve), 1, false, null, curve),
+ new(typeof(Arc), 1, false, null, curve, "autodesk.geometry.curve:circle-1.0.0"),
+ new(typeof(Circle), 1, false, null, curve, "autodesk.geometry.curve:circle-1.0.0"),
+ new(typeof(Ellipse), 1, false, null, curve, "autodesk.geometry.curve:ellipse-1.0.0"),
+ new(typeof(EllipseArc), 1, false, null, curve, "autodesk.geometry.curve:ellipse-1.0.0"),
+ new(typeof(Helix), 1, false, null, curve, "dynamo.geometry:sab-1.0.0"),
+ new(typeof(Line), 1, false, null, curve, "autodesk.geometry.curve:line-1.0.0"),
+ new(typeof(NurbsCurve), 1, false, null, curve, "autodesk.geometry.curve:bcurve-1.0.0"),
polyCurve,
polygon,
rectangle,
new(typeof(System.DateTime)),
new(typeof(double), "Number"),
new(typeof(long), "Integer"),
- new(typeof(Location)),
- new(typeof(Mesh)),
- new(typeof(Plane)),
- new(typeof(Autodesk.DesignScript.Geometry.Point)),
+ new(typeof(Location), typeId: "dynamo.data:location-1.0.0"),
+ new(typeof(Mesh), typeId: "dynamo.geometry:mesh-1.0.0"),
+ new(typeof(Plane), typeId: "autodesk.geometry.surface:plane-1.0.0"),
+ new(typeof(Autodesk.DesignScript.Geometry.Point), typeId: "autodesk.math:point3d-1.0.0"),
solid,
cone,
cylinder,
@@ -619,11 +630,11 @@ static Data()
sphere,
new(typeof(string)),
surface,
- new(typeof(NurbsSurface), 1, false, null, surface),
- new(typeof(PolySurface), 1, true, null, surface),
+ new(typeof(NurbsSurface), 1, false, null, surface, "autodesk.geometry.curve:bsurface-1.0.0"),
+ new(typeof(PolySurface), 1, true, null, surface, "dynamo.geometry:sab-1.0.0"),
new(typeof(System.TimeSpan)),
- new(typeof(UV)),
- new(typeof(Vector))
+ new(typeof(UV), typeId: "autodesk.math:uv-1.0.0"),
+ new(typeof(Vector), typeId: "autodesk.math:vector3d-1.0.0")
};
DataNodeDynamoTypeList = new ReadOnlyCollection(typeList);
diff --git a/test/DynamoCoreTests/Nodes/DefineDataTests.cs b/test/DynamoCoreTests/Nodes/DefineDataTests.cs
new file mode 100644
index 00000000000..982930d3913
--- /dev/null
+++ b/test/DynamoCoreTests/Nodes/DefineDataTests.cs
@@ -0,0 +1,59 @@
+using System.Collections.Generic;
+using System.Linq;
+using CoreNodeModels;
+using NUnit.Framework;
+
+namespace Dynamo.Tests.Nodes
+{
+ [TestFixture]
+ internal class DefineDataTests : DynamoModelTestBase
+ {
+ protected override void GetLibrariesToPreload(List libraries)
+ {
+ libraries.Add("DSCoreNodes.dll");
+ base.GetLibrariesToPreload(libraries);
+ }
+
+ [Test]
+ [Category("UnitTests")]
+ public void ValueSchemaProviderReturnsTypeId()
+ {
+ var node = new DefineData();
+ CurrentDynamoModel.CurrentWorkspace.AddAndRegisterNode(node, false);
+
+ // Find a type with a non-null TypeId for testing
+ var pointType = DSCore.Data.DataNodeDynamoTypeList.First(t => t.Type == typeof(Autodesk.DesignScript.Geometry.Point));
+ Assert.IsNotNull(pointType.TypeId, "Point type should have a valid TypeId");
+
+ // Set the node to Point type
+ node.SelectedIndex = DSCore.Data.DataNodeDynamoTypeList.IndexOf(pointType);
+
+ Assert.AreEqual(pointType.TypeId, node.ValueTypeId);
+ Assert.IsFalse(node.IsListValue);
+ }
+
+ [Test]
+ [Category("UnitTests")]
+ public void ValueTypeIdSafeWhenNoSelection()
+ {
+ var node = new DefineData();
+ Assert.DoesNotThrow(() => { var _ = node.ValueTypeId; });
+ Assert.AreEqual(node.SelectedString, node.ValueTypeId);
+ }
+
+ [Test]
+ [Category("UnitTests")]
+ public void ValueTypeIdHandlesNullTypeId()
+ {
+ var node = new DefineData();
+ CurrentDynamoModel.CurrentWorkspace.AddAndRegisterNode(node, false);
+
+ // Default selection (bool) has null TypeId
+ var firstType = DSCore.Data.DataNodeDynamoTypeList.First();
+ Assert.IsNull(firstType.TypeId, "First type (bool) should have null TypeId");
+
+ // Should fall back to SelectedString when TypeId is null
+ Assert.AreEqual(node.SelectedString, node.ValueTypeId);
+ }
+ }
+}