MPS Actions Aspect
SkillDev toolsUse when defining or editing MPS node factories (the "actions" aspect) — `NodeFactories` roots, per-concept `NodeFactory` setup functions that initialize a freshly created node and optionally copy data from a replaced `sampleNode`, plus the actions aspect's `CopyPasteHandlers` and `PasteWrappers` roots. Reach for this skill when a substitution, side transform, completion replacement, or `add new initialized(...)` should preserve fields from the node it is replacing, or when defaults set in a constructor are not enough.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the MPS Actions Aspect skill
What this skill tells your AI
The instructions your AI receives, as published by jetbrains/mps in .agents/skills/mps-aspect-actions/SKILL.md and read by ahel’s review.
The actions aspect customizes how new nodes of a concept are constructed by the MPS editor — typically during substitution, side transformations, right-transform/<ctrl-space> replacement, or auto-replace. The framework produces an empty instance of the target concept; the setup function then populates it, often copying fields from the node it is replacing (sampleNode). Lives in <lang>/models/<lang>.actions.mps, language jetbrains.mps.lang.actions. The aspect also hosts copy/paste handlers and paste wrappers (see references/copy-paste-and-paste-wrappers.md).
Critical Directives
- One
NodeFactoriesroot per actions model; add per-conceptNodeFactorychildren to it. Do not create one root per concept. - One
NodeFactoryperapplicableConcept. Multiple factories for the same concept are not merged. - The setup function runs before
newNodeis attached to the model.newNode.parent, ancestors, and model-wide queries return null/empty. Use theenclosingNodeimplicit parameter when you need the would-be parent. sampleNodeis typed asnode<BaseConcept>. Always narrow withifInstanceOf (sampleNode is <Concept> original)before accessing fields — direct dereference will NPE or fail to compile.- Use
SPropertyAccess(refproperty→PropertyDeclaration) for properties; useSLinkAccess(reflink→LinkDeclaration) for both containment and reference child roles. - Factories do not fire for quotations
<C()>,model.add root(<C()>),model.add new root(C), or pure rendering. If you need factory-initialised nodes in generator code, usenew node<C>()with explicit property assignments or route creation through an intention that callsadd new initialized. - Edit factories through MPS MCP tools (
mps_mcp_insert_root_node_from_json,mps_mcp_update_node, optionallymps_mcp_parse_java_and_insertwithfeatureKind: "STATEMENTS"for the body). Do not hand-edit.mpsfiles. - After editing, validate with
mps_mcp_check_root_node_problemson theNodeFactoriesroot and rebuild the language.
Common-Path Workflow
- Ensure an actions model exists (
<lang>/models/<lang>.actions.mps; create withmps_mcp_create_modelandmodelName: "<lang>.actions"— aspect IDactions, case-sensitive, no@suffix; see aspect-model-stereotypes.md). Used languages:jetbrains.mps.lang.actions,jetbrains.mps.baseLanguage,jetbrains.mps.lang.smodel. Addjetbrains.mps.lang.coreas the base. Import the structure model of your language. - Create the
NodeFactoriesroot viamps_mcp_insert_root_node_from_json(blueprint inreferences/json-blueprints.md). Setname. - For each concept that needs custom initialization, add a
NodeFactorychild viamps_mcp_update_node. SetapplicableConcept; attach aNodeSetupFunctionwith aStatementListbody. - Fill the body. Typical pattern:
ifInstanceOf (sampleNode is <Concept> original) { newNode.<prop> = original.<prop>; ... }. Cross-type narrowing is allowed — seereferences/setup-function-bodies.md. - Validate with
mps_mcp_check_root_node_problems, rebuild the language, exercise in a sandbox.
Implicit Parameters of NodeSetupFunction
| Alias | FQN | Type | Nullable | Notes |
|---|---|---|---|---|
newNode | jetbrains.mps.lang.actions.structure.NodeSetupFunction_NewNode | node<applicableConcept> | no | The freshly created empty node. Populate it. |
sampleNode | jetbrains.mps.lang.actions.structure.NodeSetupFunction_SampleNode | node<BaseConcept> | yes | Node being replaced, if any. Narrow with ifInstanceOf. |
enclosingNode | jetbrains.mps.lang.actions.structure.NodeSetupFunction_EnclosingNode | node<BaseConcept> | yes | Prospective parent container; null during root-node creation or programmatic creation outside the editor. |
index | jetbrains.mps.lang.actions.structure.NodeSetupFunction_Index | int | — | 0-based position in the parent collection, or 0 if not in a collection. |
model | (plain VariableReference in generated Java) | SModel | yes | The SModel newNode will be inserted into. Most factories don't use this. |
Signature surfaced in the editor: (newNode, sampleNode, enclosingNode, index, model) -> void.
Related Skills
mps-aspect-behavior— concept constructors are the place to set defaults that don't depend on the node being replaced. Use a constructor for intrinsic defaults, aNodeFactoryfor context-sensitive carry-over.mps-aspect-intentions— intentions that calladd new initialized(...)route through factories. TheNF_*family is documented in the intentions skill'sreferences/factory-initialized.md.mps-aspect-generator— generator output bypasses factories (quotations andadd new rootdo not fire factories). Document where the generator must mimic the factory behavior.mps-model-manipulation— full smodel reference:SLinkAccessvsSPropertyAccess,IfInstanceOfStatement,IfInstanceOfVariable, and theNF_*family.mps-aspect-structure-concepts— when adding the concept thatapplicableConcepttargets.mps-aspect-constraints— forcanBe*rules that gate insertion before a factory ever runs.
Reference Index
- Open
references/node-factories-and-triggers.mdwhen you need the validated concept ref forNodeFactories/NodeFactory/NodeSetupFunction, the rule that factories are picked by exactapplicableConcept(not by subtype inheritance through a more-general parent factory), and the precise list of triggers that fire / do not fire a factory. - Open
references/setup-function-bodies.mdwhen writing the function body — copying properties vs. child links (SPropertyAccessvsSLinkAccess), cross-type narrowing inifInstanceOf, the verbatim ChemMastery (same-type property copy) and Kaja (cross-type child-link copy) examples, and the JSON shape for anAssignmentExpressionoverSLinkAccess. - Open
references/json-blueprints.mdwhen inserting theNodeFactoriesroot or aNodeFactorychild via MCP — minimal blueprints, the fullIfInstanceOfStatementbody, and the tip on usingmps_mcp_parse_java_and_insertto skip the blueprint. - Open
references/copy-paste-and-paste-wrappers.mdwhen the language needs custom copy pre-processing, paste post-processing, or paste-time wrapping of nodes — these other roots also live in the actions aspect. - Open
references/concept-reference.mdfor the validated FQN/ref table — thejetbrains.mps.lang.actionsconcepts plus the supporting BaseLanguage and smodel concepts used in setup bodies. - Open
references/common-failures.mdwhen a freshly created node always has defaults,sampleNode.<prop>does not compile,enclosingNodeis unexpectedly null, the factory fires twice, or it does not fire from quotations / generator code.
Signals
- GitHub stars
- 2k
- Forks
- 309
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
mps-aspect-actions- Source
- github.com/jetbrains/mps