# Mps Aspect Behavior

> Use when defining or editing MPS `ConceptBehavior` — per-concept methods (non-virtual / virtual / abstract / static / virtual static), constructors, virtual dispatch (MRO), super and interface-default calls (`super<Interface>.method`), overriding methods from `lang.core.behavior` interfaces such as `ScopeProvider.getScope` / `INamedConcept.getName` / `BaseConcept.getPresentation`, calling sibling methods (`LocalBehaviorMethodCall`) and behavior methods from other aspects via `node.method(...)`. Reach for this skill whenever the task involves authoring or modifying `<lang>/languageModels/behavior.mps`.

- Skill: `jetbrains/mps-aspect-behavior` (Agent Skill, multi-file: 9 files)
- Install (CLI): `npx skillmds@latest add jetbrains/mps-aspect-behavior`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jetbrains/mps-aspect-behavior/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: jetbrains (https://skillmd.com/u/jetbrains)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/jetbrains/mps-aspect-behavior

---


# MPS Behavior Aspect

The **behavior** aspect attaches methods and a constructor to a concept, much like adding methods to a Java class. Bodies are written in BaseLanguage + smodel and are callable from any other aspect (editor, constraints, typesystem, generator, intentions, plugin) via `node.methodName(...)`. Lives in `<lang>/languageModels/behavior.mps`, language `jetbrains.mps.lang.behavior`.

## Critical Directives

- One `ConceptBehavior` root per concept. The `constructor` child is **mandatory** (an empty body is fine).
- Constructors run **before** the node is attached to the model. `parent`, ancestors, and all children/descendants evaluate to `null`. Constructors can only set own property/reference defaults and add mandatory children — use a `NodeFactory` (actions aspect) for anything that needs the parent/model context.
- Quotations (`<C()>`) bypass behavior constructors. Use `new node<C>()` or `add new initialized C` when you need the constructor to fire.
- Constructors are **not** inherited — each concept has at most one, and they do not chain (no `super()`). MPS runs each ancestor's constructor in ancestor-first order independently.
- An overriding method must match the signature of the supertype/interface method exactly. When implementing an interface method (e.g. `ScopeProvider.getScope`), set `overriddenMethod` on the new method to the interface declaration — MPS uses it for dispatch and signature validation.
- Behavior calls on a `null` node do **not** NPE — MPS returns `null` for reference/String returns and the default for primitives. Still handle the `null` return for reference types.
- Hoist shared methods to an abstract super-concept rather than duplicating per-sub-concept `ConceptBehavior` roots — virtual dispatch covers all non-abstract sub-concepts. (See `references/inheritance-and-dispatch.md`.)
- Methods are **non-virtual by default**: all modifier booleans (`isVirtual`, `isAbstract`, `isStatic`, `isFinal`) default to `false`. Only `virtual` methods can be overridden; mark a method `virtual` up front if sub-concepts may ever need to specialise it.
- When the same piece of node-handling logic repeats across several places or several aspects of one concept (editor, constraints, typesystem, generator, intentions, textgen), **extract it into a behavior method** and call it everywhere via `node.m(...)` — this de-duplication is the behavior aspect's primary purpose. Pick the modifier with the table below.
- Edit behavior models through MPS MCP tools (`mps_mcp_insert_root_node_from_json`, `mps_mcp_update_node`, `mps_mcp_parse_java_and_insert`). Do not hand-edit `.mps` files.
- For MPS-typed return types (`sequence<node<X>>`, `list<node<X>>`), `mps_mcp_parse_java_and_insert` produces Java `List<SNode>` — replace `returnType` afterwards with the correct MPS blueprint (see `mps-model-manipulation/references/variable-declarations.md`).
- After edits run `mps_mcp_check_root_node_problems` and rebuild the language.

## Common-Path Workflow

1. Create a `behavior` model if missing (`mps_mcp_create_model` with `modelName: "<lang>.behavior"` — aspect ID `behavior`, case-sensitive, no `@` suffix; see [aspect-model-stereotypes.md](../mps-mcp-workflow/references/aspect-model-stereotypes.md)). Use languages: `jetbrains.mps.lang.behavior`, plus any languages referenced in bodies (`smodel`, `collections`, `closures`, `baseLanguage`).
2. Add a `ConceptBehavior` root for the target concept; set `concept` ref. The minimal blueprint (with the mandatory empty `constructor`) is in `references/json-blueprints.md`.
3. Add `ConceptConstructorDeclaration` (at most one) and/or `ConceptMethodDeclaration` children.
4. Write bodies. For non-trivial logic prefer `mps_mcp_parse_java_and_insert` with `featureKind: "STATEMENTS"` or `"METHOD"`, then fix MPS-typed return/parameter types.
5. Validate with `mps_mcp_check_root_node_problems`, rebuild the language.

## Method Modifier Quick Reference

| Modifier | Call shape | Dispatch / inheritance | Notes |
|---|---|---|---|
| *(none)* — non-virtual (**default**) | `node.m(...)` | Inherited by sub-concepts but **statically bound — cannot be overridden** | A same-named method in a sub-concept's behavior shadows it (hazard) instead of overriding. Most `Classifier_Behavior` utilities in baseLanguage are non-virtual. |
| `virtual` | `node.m(...)` | Dispatched at runtime by the node's actual concept; overridable in sub-concept behaviors | Set `isVirtual: true` explicitly. E.g. `Expression.isLValue` and every method of `Type_Behavior` in baseLanguage. |
| `abstract` | `node.m(...)` (virtually dispatched) | No body; every non-abstract sub-concept must provide an implementation | **Implies `virtual` — set both `isAbstract` and `isVirtual`.** Declare on abstract/interface concepts (e.g. `Classifier.findAncestor`, `IMemberContainer.getMembers` in baseLanguage). |
| `final` | `node.m(...)` | A virtual method that cannot be overridden further down | Rarely needed — a plain non-virtual method is already non-overridable. |
| `static` | `ConceptName.m(...)` (qualified by concept name) | Belongs to the concept; no `this`; no dispatch | Concept-wide utilities, e.g. `Classifier.getContextClassifier` in baseLanguage. Called as `LocalBehaviorMethodCall` when unqualified inside the same `ConceptBehavior`. |
| `virtual static` | `conceptValue.m(...)` on a `concept<X>` expression | Dispatched by the runtime **concept value**; overridable in sub-concept behaviors (link `overriddenMethod`) | Per-concept (not per-node) polymorphism — e.g. `Expression.getPrecedenceLevel`, `Type.isValueType` in baseLanguage, overridden across the smodel/collections behaviors. |

### Choosing a modifier for an extracted method

- Invoked on a **node** (needs `this`, properties, children)? → instance method: `virtual` if sub-concepts must be able to override it (or the logic varies by concept), non-virtual for a fixed helper.
- Invoked on a **concept** (no node instance at hand, concept-wide utility, or nodes passed as parameters)? → `static`; make it `virtual static` when the result must vary per concept (dispatch on a `concept<X>` value).
- Shared by several sub-concepts? → declare it once on their common (abstract) super-concept or interface; use `abstract` (+ `virtual`) when only sub-concepts can supply the body.

## Related Skills

- `mps-aspect-actions` — `NodeFactory` is the right place for initialization that needs parent/model context (constructors run before the node is attached).
- `mps-aspect-intentions`, `mps-aspect-constraints`, `mps-aspect-generator`, `mps-aspect-typesystem` — call behavior methods via `node.m(...)`; behavior is one of the most-used aspects from these.
- `mps-model-manipulation` — full BaseLanguage / smodel / collections reference. Covers the `LinkList_AddNewChildOperation` family and the `List<SNode>` → `sequence<node<X>>` return-type fix.
- `mps-aspect-constraints` — `ScopeProvider.getScope` is most often overridden in a behavior; constraints describe where the reference lives.
- `mps-aspect-structure-concepts` — when introducing the concept that the behavior attaches to.
- `mps-language-inheritance` — to design the abstract-super-concept refactors discussed in `references/inheritance-and-dispatch.md`.
- `mps-quotations` — quotations bypass behavior constructors; document the workaround in the generator.

## Reference Index

- Open `references/method-declarations.md` when authoring a `ConceptMethodDeclaration` — modifiers, parameters, `overriddenMethod`, `BaseConcept` overrides (`getPresentation` / `getSideIcon`), implicit-return rule, and the MPS-typed return-type Java-parser caveat.
- Open `references/local-and-super-calls.md` when calling sibling methods of the same `ConceptBehavior` (`LocalBehaviorMethodCall`), distinguishing unqualified `m(args)` from `this.m(args)` (which generates `DotExpression { ThisNodeExpression, Node_ConceptMethodCall }`), or calling `super<Interface>.method(...)` — includes the verbatim Stateful and Kaja `CommentLine_Behavior` examples and the `Stateful_Behavior.getScope` super-call JSON.
- Open `references/inheritance-and-dispatch.md` when designing or debugging virtual dispatch — the MRO algorithm (own → extended super → interfaces in definition order), how a method on an abstract super-concept covers every concrete sub-concept (Stateful pattern), and when to hoist shared behavior up vs. duplicate per concept.
- Open `references/constructors.md` when writing a `ConceptConstructorDeclaration` — what runs and what does not run a constructor, the "node not yet attached" constraint, scalar-default and seed-mandatory-child examples (ChemMastery `Compound_Behavior` and `ChemEquation_Behavior`) with full JSON blueprints, and the surface→FQN mapping for the surface syntax involved.
- Open `references/calling-from-other-aspects.md` when calling a behavior method from generator templates, typesystem rules, constraints, intentions, or hand-written Java — the descriptor invocation shape, the `_idXXXXX` stable suffix, and the null-safety behavior of `node.method()`.
- Open `references/json-blueprints.md` when inserting a behavior root or a method via MCP — minimal `ConceptBehavior` with the mandatory empty constructor, the `ConceptMethodDeclaration` skeleton, and the validated concept ref.
- Open `references/lang-core-behavior-overrides.md` for the validated node refs to set as `overriddenMethod` when implementing common `lang.core.behavior` methods (`getTextualRepresentation`, `isTODOComment`, `getScope`), plus the `IGenericComment` interface node ref used in `super<IGenericComment>.isTODOComment()` calls.
- Open `references/common-failures.md` when a behavior method isn't found, an override is ignored, a constructor's defaults don't stick, a descriptor returns `Object`, the return type fights the Java parser, or quotations bypass the constructor.

