Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Xpath Constraints

ASecurity

XPath constraint syntax for MDL — retrieve WHERE clauses, page data sources, and row-level entity access, including association paths and functions. Use when writing or debugging any XPath in a project.

128 stars
0 votes
0 copies
0 views
Added 9/26/2026
ai-agentsgobashsqlexpressdebuggingdatabasesecurity

Works with

cli

Security Analysis

A100/100

Scanned 10/4/2026

$npx -y skills add mendixlabs/mxcli --skill xpath-constraints --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Xpath Constraints?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Xpath Constraints
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mendixlabs-xpath-constraints/badge)](https://www.skillsdirectory.com/skills/mendixlabs-xpath-constraints)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: xpath-constraints
description: "XPath constraint syntax for MDL — retrieve WHERE clauses, page data sources, and row-level entity access, including association paths and functions. Use when writing or debugging any XPath in a project."
---

# XPath Constraints in MDL

This skill provides reference for writing XPath constraint expressions in MDL RETRIEVE statements, page data sources, and security rules.

## When to Use This Skill

- Writing `retrieve ... where [xpath]` statements in microflows
- Writing `database from entity where [xpath]` in page data sources
- Writing `grant ... where 'xpath'` for row-level entity access
- Debugging XPath parsing or serialization issues

## XPath vs Mendix Expressions

**Critical distinction**: XPath constraints (inside `[...]`) use different syntax from Mendix expressions (in SET, IF, DECLARE, etc.):

| Feature | XPath `[...]` | Mendix Expression |
|---------|---------------|-------------------|
| Path separator | `/` (always path traversal) | `/` (also division) |
| Boolean ops | lowercase: `and`, `or`, `not()` | `and`, `or`, `not` |
| Negation | `not(expr)` function | `not expr` |
| Empty check | `= empty`, `!= empty` | `= empty` |
| Token quoting | `'[%CurrentUser%]'` (quoted) | `[%CurrentUser%]` (unquoted) |
| Nested filter | `Assoc/entity[pred]` | Not applicable |
| Arithmetic on value | **not supported** — pre-compute into a variable | `+`, `-`, `*`, `div`, `mod` |

> **XPath constraints cannot compute values.** `where [Seq = $Game/MoveSeq + 1]`
> is a parse error (Mendix XPath has no arithmetic on the value side). Compute the
> value first, then compare against the variable:
> ```mdl
> declare $Next integer = $Game/MoveSeq + 1;
> retrieve $M from Mod.Move where [Seq = $Next] first;
> ```
> `mxcli check` explains this and shows the workaround when it sees `+`/`*`/`div`/
> `mod` inside a constraint.

> **A negative literal is fine, though.** A leading `-` on a number is a value,
> not arithmetic, and needs no quoting:
> ```mdl
> retrieve $L from Mod.T where [Amount > -7];
> retrieve $L from Mod.T where [Amount <= -12.5 and Code != 'X'];
> ```

> **Date arithmetic is not available in XPath.** `addDays()`, `addMonths()` and
> friends are *Mendix expression* functions — using one in a constraint fails the
> build with `CE0161` regardless of its arguments. For relative dates use the
> date tokens (`[%CurrentDateTime%]`, `[%BeginOfCurrentDay%]`, …), or compute the
> cut-off in a variable first and compare against that:
> ```mdl
> declare $Cutoff datetime = addDays([%CurrentDateTime%], -7);
> retrieve $L from Mod.T where [DueDate > $Cutoff];
> ```

## Syntax Reference

### Simple Comparisons

```mdl
retrieve $Orders from Module.Order
  where [State = 'Completed'];

retrieve $Active from Module.Customer
  where [IsActive = true];

retrieve $Recent from Module.Order
  where [OrderDate != empty];

retrieve $HighValue from Module.Order
  where [TotalAmount >= $MinAmount];
```

Operators: `=`, `!=`, `<`, `>`, `<=`, `>=`

> **Inline vs quoted form.** The inline bracket form `where [State = 'Completed']`
> is preferred. The quoted form — `where '[State = ''Completed'']'`, with internal
> single quotes doubled (`''`) — is also accepted for `retrieve` and datasource
> `where` clauses, and now stores the identical constraint (it un-escapes the `''`
> and strips the outer quotes). Don't double-bracket: write either `[...]` or
> `'[...]'`, not both.

### Boolean Logic

```mdl
-- AND
where [State = 'Completed' and IsPaid = true]

-- OR
where [State = 'Pending' or State = 'Processing']

-- Grouped
where [State = 'Completed' and ($IgnorePaid or IsPaid = true)]

-- NOT
where [not(IsPaid)]
where [not(contains(Name, 'demo'))]
```

## How a Constraint Is Laid Out on Disk

MDL keeps a constraint on one line; how it is **stored** is decided by mxcli, not
by the whitespace you type. A constraint is rebuilt from its parse tree on every
write, so there is no original formatting to keep — instead the layout is derived
from the expression:

- **80 columns or fewer** → stored exactly as written, on one line. This is the
  common case, and it means adding this changed nothing about existing projects.
- **Longer** → broken at its top-level `and`/`or` joints, one clause per line,
  the operator leading each continuation line. Where `and` and `or` meet, the
  `and` runs get explicit parentheses — Mendix binds `and` tighter, and a filter
  is being broken up precisely because it had stopped being obvious at a glance.
- **Nothing to break on** (one long comparison, one long association path) →
  left whole and over width. Cutting it anywhere else would not be valid XPath.

```
-- authored (one line, 141 characters)
where [Archived = false and Status = 'Open' and Priority = 'High' and Category = 'Electrical' and Severity > 3 and ReportedOn > '[%BeginOfCurrentDay%]']

-- stored, and what Studio Pro's XPath editor shows
[
  Archived = false
  and Status = 'Open'
  and Priority = 'High'
  and Category = 'Electrical'
  and Severity > 3
  and ReportedOn > '[%BeginOfCurrentDay%]'
]
```

`DESCRIBE` puts it back on one line, so a description reads the way it always
has and re-executing it re-derives the same stored text — the unit is reported
`Unchanged`. A constraint mxcli cannot parse is stored exactly as given rather
than reformatted.

This applies to page datasources, `retrieve … where` in microflows, and entity
access rules alike.

### Association Path Traversal

Bare association paths (without `$variable` prefix) navigate through the domain model:

```mdl
-- Single-hop: filter by associated object
where [Module.Order_Customer = $Customer]

-- Multi-hop: traverse through associations
where [Module.Order_Customer/Module.Customer/Name = $CustomerName]

-- Existence check: has an associated object
where [Module.Order_Customer/Module.Customer]

-- Negated existence: has NO associated object
where [not(Module.Order_Customer/Module.Customer)]
```

**Rule**: Always use the fully qualified association name (`Module.AssociationName`).

> **A bare association name is now caught before the build (MDL-XPATH01).**
> `[Order_Customer = $currentUser]` used to pass `mxcli check --references`, get
> written by `exec`, and only fail at the build with *"Error(s) in XPath
> constraint"* (**CE0161**) — which is the expensive shape, because `exec` cannot
> roll back and stops with the model half-updated. `check` now names the
> association and the qualified spelling to use instead. It fires only when the
> bare name is not an attribute of the constrained entity **and** is a known
> association, so attributes stay bare and XPath functions are never touched.

> **`= empty` / `!= empty` do not work on associations (CE0161 / MDL047).**
> `empty` tests *attribute* nullability only. To test whether an object *has no*
> associated object, use negated existence: `[not(Module.Order_Customer/Module.Customer)]`
> — **not** `[Module.Order_Customer = empty]`; to test that it *has* one, the path
> itself: `[Module.Order_Customer/Module.Customer]` — **not** `!= empty`.
> `mxcli check` flags both forms as **MDL047** before the build does.

### Variable Paths

```mdl
-- Compare attribute via variable path
where [Module.Assoc/Module.Entity/Name = $Variable/Name]

-- Variable on right side
where [Name = $currentObject/SearchString]
```

### Nested Predicates

Filter intermediate path steps with inline `[predicate]`:

```mdl
-- Only lines of completed orders
where [Module.OrderLine_Order/Module.Order[State = 'Completed']]

-- Nested predicate with further traversal
where [Module.OrderLine_Order/Module.Order[State = 'Active']/Module.Order_Category/Module.Category/Name = $CategoryName]

-- reversed() path modifier (traverse association in reverse direction)
where [System.grantableRoles[reversed()]/System.UserRole/System.UserRoles = '[%CurrentUser%]']
```

### Functions

```mdl
-- String search
where [contains(Name, $SearchStr)]
where [starts-with(Name, $Prefix)]
where [not(contains(Name, 'demo'))]

-- Boolean functions
where [IsActive = true()]
where [Displayed = false()]
```

Supported functions: `contains()`, `starts-with()`, `not()`, `true()`, `false()`

The expression functions `startsWith()` / `endsWith()` are not XPath: in a
constraint they are CE0161, and `check` reports them as **MDL091**. A member the
entity does not have is a reference error in `check -p`, and so is a system
member written the way `describe` prints the attribute: XPath spells it
`createdDate`, `changedDate`, `owner`, `changedBy` — `[CreatedDate > …]` is CE0161.

### Tokens

Mendix tokens provide runtime values. In an XPath constraint a token used as a
value is stored quoted as `'[%Token%]'` (Studio Pro requires this, or it reports
CE0161). mxcli quotes it for you whether you write the bare or quoted form:

```mdl
-- Both store identically as '[%CurrentDateTime%]' and pass mx check
where [OrderDate < [%CurrentDateTime%]]
where [OrderDate < '[%CurrentDateTime%]']
where [System.owner = '[%CurrentUser%]']
```

> **Tokens are typed.** `[%CurrentUser%]` is a **User** reference — compare it only
> to an association to System.User (e.g. `System.owner`), never to a String/other
> attribute (`[Title = '[%CurrentUser%]']` is a type error → CE0161).
> `[%CurrentDateTime%]` compares to DateTime attributes, etc.

> **`System.owner` / `System.changedBy` must be enabled** on the entity before you
> can reference them in XPath, or Studio Pro reports CE0161. Enable with
> `alter entity Module.Entity add attribute owner: autoowner;` (mxcli's
> `check --references` flags this). Same for `changedBy`/`changedDate`/`createdDate`.

Common tokens: `[%CurrentUser%]`, `[%CurrentDateTime%]`, `[%CurrentObject%]`, `[%UserRole_RoleName%]`, `[%DayLength%]`

### ID Pseudo-Attribute

The `id` pseudo-attribute compares object identity (GUID):

```mdl
where [id = $currentUser]
where [id != $existingObject]
where [id = '[%CurrentUser%]']
```

## Usage Contexts

### RETRIEVE in Microflows

```mdl
retrieve $Results from Module.Entity
  where [IsActive = true and State = 'Ready']
  sort by Name asc
  limit 100;
```

The expression inside `[...]` is parsed as XPath and stored in BSON as the `XpathConstraint` field.

### Page Data Sources

```mdl
datagrid dg (
  datasource: database from Module.Entity where [State != 'Cancelled'] sort by Name asc
) {
  column (attribute: Name, caption: 'Name')
}
```

Multiple bracket constraints can be chained. Consecutive brackets without an operator are treated as AND (standard Mendix XPath):

```mdl
-- Consecutive brackets (implicit AND) — standard Mendix XPath syntax
datasource: database from Module.Entity where [IsActive = true][Stock > 0]

-- Explicit AND: same result
datasource: database from Module.Entity where [IsActive = true] and [Stock > 0]

-- Mix with OR: combines into single bracket
datasource: database from Module.Entity where [IsActive = true] or [Stock > 10]
```

### GRANT Entity Access (Security)

Security rules take the XPath in brackets, like every other XPath, so quotes
inside it are written once:

```mdl
mdl 1;
grant read *, write * on entity Module.Entity to Module.Role
  where [System.owner = '[%CurrentUser%]'];
```

Sibling groups (`where [a][b]`) are kept as one constraint. The old quoted form
`grant Module.Role on Module.Entity (...) where '[...]'` still parses, warns
MDL-DEPR030, and `mxcli fmt --upgrade` rewrites it.

## Enumeration Attributes

**Critical**: XPath constraints are translated to database SQL WHERE clauses at runtime. The database stores enum values as plain strings (the value key), not qualified names. This means:

- `[Status = 'Open']` — always valid: direct string literal match
- `[Status = Module.OrderStatus.Open]` — also valid: mxcli converts to `'Open'` in BSON automatically

Both forms are accepted by mxcli in the write direction. `DESCRIBE MICROFLOW` always shows the qualified name form for readability, even though BSON stores `'Open'`.

**Do NOT use qualified names in expression context (IF, SET, DECLARE) for comparisons** — those contexts use a different form. See `write-microflows` "Enumeration Comparisons" section.

```mdl
-- Preferred (mxcli converts to 'Open' in BSON):
retrieve $OpenOrders from Module.Order
  where [Status = Module.OrderStatus.Open];

-- Also accepted (stored as-is):
retrieve $OpenOrders from Module.Order
  where [Status = 'Open'];

-- NOT equal
retrieve $Active from Module.Order
  where [Status != Module.OrderStatus.Cancelled];

-- OR across multiple enum values
retrieve $InProgress from Module.Order
  where [Status = Module.OrderStatus.Open or Status = Module.OrderStatus.Processing];

-- Enum combined with other predicates
retrieve $Results from Module.Order
  where [Status = Module.OrderStatus.Completed and TotalAmount >= $MinAmount];
```

### Troubleshooting silent empty results with enums

If a RETRIEVE returns empty unexpectedly when filtering by an enum attribute:
1. Check the **value key** (not caption) — the key is what's stored in the DB column. Check with `DESCRIBE ENUMERATION Module.EnumName`.
2. Keys are **case-sensitive**: `'open'` ≠ `'Open'`.
3. Confirm the attribute type is actually an enumeration and not a string — `DESCRIBE ENTITY Module.EntityName`.

## Common Patterns

### Parameterized Search

```mdl
mdl 1;
create microflow Module.Search ($query: string, $ActiveOnly: boolean)
returns boolean
begin
  retrieve $Results from Module.Customer
    where [($ActiveOnly = false or IsActive = true)
      and (contains(Name, $query) or contains(Email, $query))];
  return true;
end;
```

### Date Range Filter

```mdl
retrieve $Orders from Module.Order
  where [OrderDate >= $StartDate and OrderDate <= $EndDate];
```

### Optional Filters (empty = skip)

```mdl
retrieve $Orders from Module.Order
  where [($Category = empty or Module.Order_Category = $Category)
    and ($State = empty or State = $State)];
```

### Owner-Based Security

```mdl
-- In microflow
retrieve $MyItems from Module.Item
  where [System.owner = '[%CurrentUser%]'];

-- In security rule
grant read * on entity Module.Item to Module.User where [System.owner = '[%CurrentUser%]'];
```

## Validation

Always validate XPath syntax before execution:

```bash
# Syntax check (no project needed)
./bin/mxcli check script.mdl

# with reference validation (needs project)
./bin/mxcli check script.mdl -p app.mpr --references
```

## Troubleshooting

| Issue | Cause | Fix |
|-------|-------|-----|
| `mismatched input` on keyword | Attribute name is a reserved word | This is handled — `xpathWord` accepts any keyword as identifier |
| Token not quoted in BSON | Token in Mendix expression context | Use `[...]` bracket syntax for XPath, not bare expression |
| `CE0111` path error | Missing module prefix on association | Use `Module.AssociationName`, not just `AssociationName` |
| `CE0161` XPath constraint error | Qualified name used for non-enum or wrong format | Use string literal `'Value'` or qualified name `Module.Enum.Value`; mxcli converts automatically |
| `not` parsed as keyword | Using `not` (uppercase) in XPath | XPath uses lowercase `not()` as a function |
| Retrieve returns empty for enum filter | String literal value key mismatch | Key is case-sensitive; verify with `DESCRIBE ENUMERATION Module.Name` |

Attribution

mendixlabsmendixlabs
View sourceSee grades on GitHubMore from mendixlabs →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Terse caveman voice: answer first, fluff gone, every technical fact kept. Use for /caveman, "caveman mode", "talk like caveman", "be brief", "less tokens". Stays on until "stop caveman" or "normal mode".

1100021 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

698461 votes

Writing Skills

Create and manage Claude Code skills in HASH repository following Anthropic best practices. Use when creating new skills, modifying skill-rules.json, understanding trigger patterns, working with hooks, debugging skill activation, or implementing progressive disclosure. Covers skill structure, YAML frontmatter, trigger types (keywords, intent patterns), UserPromptSubmit hook, and the 500-line rule. Includes validation and debugging with SKILL_DEBUG. Examples include rust-error-stack, cargo-dep...

3931 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3421 votes

catchup

Recovers the conversation and failed tool calls of a previous Codex, Amp, Claude Code, Antigravity, Cline, Copilot CLI, Cursor, DeepSeek Harness, Grok Build, Kimi, OpenCode, Pi Agent, or ZCode session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", asks to recover/summarize a previous session before continuing, or asks to diagnose or report a catchup failure. Do NOT use for the current conversation, git history, or any non-agent log.

741 votes
View all in ai-agents →