How can MODX adapt to AI-native / agentic development environments?

Hi all,

I’m wondering how MODX fits with the new wave of AI-native development tools. I’ve been experimenting with platforms like Antigravity and Cursor, where AI agents can build and maintain an entire website or app directly inside the environment.

With React/Next/Vercel these tools work great because everything is in a predictable file structure. But with MODX it seems harder… you obviously don’t want an AI agent rewriting core MODX files, while the things you do want to change (resources, snippets, chunks, TVs) are stored in the database rather than in a filesystem the agent can easily reason about.

A few questions:

  • Is anyone currently building MODX projects inside Antigravity, Cursor, or similar AI-native IDEs?

  • Could MODX evolve in a way that makes it easier for AI agents to work with MODX projects?

  • Is this something MODX needs to address to stay competitive, or am I overestimating how disruptive these agentic tools will be?

Curious to hear the community’s thoughts.

1 Like

I am busy starting a project where I am going to try do as much as possible with Cursor to see what is possible. plan is to use modx-cli will sewe how it goes

I’m skeptical about AI as a requirement, but it is handy as a tool. Have you tried out MOD AI?

I am using a combination of MODX Cloud + Git Package Management (GPM) and Cursor to have AI build me total MODX elements and features. (guide)

It really works great, I can tell it to setup a FAQ page with MIGX and a optional header image or basically anything I need.

I have static files for my chunks, templates, snippets and plugins so AI can edit and read them:

GPM creates a gpm.yaml file that looks like this:

name: aiDevModx
version: 0.0.1
lowCaseName: aidevmodx
namespace: aiDevModx
description: "This is a AI DEV MODX package for MODX 3"
author: mpietersen
build:
  license: core/components/aidevmodx/docs/license.txt
  changelog: core/components/aidevmodx/docs/changelog.txt
  readme: core/components/aidevmodx/docs/readme.txt
  requires:
    Ace: ">=1.9.3"
    Babel: ">=3.5.2"
    Call to Action TV: ">=3.0.2"
    ClientConfig: ">=2.5.0"
    Collections: ">=4.1.1"
    ColorPicker: ">=2.0.5"
    FormIt: ">=5.1.1"
    Image+: ">=2.9.4"
    MIGX: ">=3.0.2"
    pdoTools: ">=3.0.2"
    pThumb: ">=2.3.3"
    Resizer: ">=1.0.2"
    SEO Suite: ">=3.1.9"
    SimpleSearch: ">=3.1.0"
    TinyMCE Rich Text Editor: ">=3.1.5"
menus:
  - text: aidevmodx.menu
    action: manage
categories:
  - name: general
    rank: 1
  - name: blog
    rank: 2
  - name: faq
    rank: 3
  - name: contact
    rank: 4
  - name: request
    rank: 5
  - name: navigation
  - name: social
  - name: search
  - name: Page options
  - name: Breadcrumb
  - name: Sidebar
  - name: Content blocks
  - name: homepage
    children:
      - name: Hero section
        rank: 1
      - name: Feature section
        rank: 2
      - name: Bento section
        rank: 3
      - name: Blog section
        rank: 4
      - name: CTA section
        rank: 5
templates:
  - name: homepage
    description: "Homepage template."
  - name: basic-page
    description: "General page template."
  - name: blog-overview-page
    description: "Blog listing page template."
  - name: blog-detail-page
    description: "Blog article detail page template."
    properties:
      - name: contentblocks.disabled
        value: "0"
        type: textfield
  - name: faq-page
    description: "FAQ page template with accordion functionality."
  - name: contact-page
    description: "Contact page template."
  - name: request-page
    description: "Request page template."
  - name: search-results-page
    description: "Search results page template."
  - name: overview-page
    description: "Overview page template."
  - name: content-block-page
    description: "Content block page template."
    properties:
      - name: contentblocks.disabled
        value: "0"
        type: textfield
  - name: demo-page
    description: "Demo page template."
snippets:
  - name: fileWithVersionTimeStamp
chunks:
  - name: scripts
    description: "Scripts on all pages"
    category: general
  - name: header
    description: ""
    category: general
  - name: header
    description: ""
    category: general
  - name: head
    description: ""
    category: general
  - name: main_nav_parent_row
    description: "Main navigation parent row"
    category: navigation
  - name: footer
    description: ""
    category: general
  - name: home_image_tpl
    description: "Home image"
    category: Hero section
  - name: blog_row_tpl
    description: "Blog post card for listing pages"
    category: blog
  - name: faq_accordion_item
    description: "Individual FAQ accordion item"
    category: faq
  - name: faq_accordion_wrapper
    description: "FAQ accordion container wrapper"
    category: faq
  - name: product_dropdown
    description: "Product dropdown menu for header navigation"
    category: general
  - name: mobile_menu
    description: "Mobile navigation menu with slide-out panel"
    category: general
  - name: main_menu
    description: "Main navigation menu"
    category: general
  - name: contact_form
    description: "Outer code for a contact form"
    category: contact
  - name: request_form_inner
    description: "Request form inner"
    category: request
  - name: blog_image_main_tpl
    description: "Blog image main"
    category: blog
  - name: blog_image_overview_tpl
    description: "Blog image overview"
    category: blog
  - name: blog_image_latest_tpl
    description: "Blog image latest"
    category: blog
  - name: blog_latest_row_tpl
    description: "Blog latest row"
    category: blog
  - name: social_media_icons
    description: "Social media icons"
    category: social
  - name: search_popup
    description: "Search popup"
    category: search
  - name: search_toggle
    description: "Search toggle"
    category: search
  - name: search_tpl
    description: "Search form template"
    category: search
  - name: search_input_above_tpl
    description: "Search form template above the results"
    category: search
  - name: search_result_tpl
    description: "Search result template"
    category: search
  - name: search_result_container
    description: "Search result container"
    category: search
  - name: search_result_page_tpl
    description: "Search result page template"
    category: search
  - name: search_result_current_page_tpl
    description: "Search result current page template"
    category: search
  - name: search_no_results_tpl
    description: "Search no results template"
    category: search
  - name: home_cta_link_chunk
    description: "Home CTA link chunk"
    category: CTA section
  - name: home_cta_link_2_chunk
    description: "Home CTA link 2 chunk"
    category: CTA section
  - name: bento_section_1_cta_link_chunk
    description: "Bento section 1 CTA link chunk"
    category: Feature section
  - name: cta_box_link_chunk
    description: "CTA box link chunk"
  - name: cta_banner_button_chunk
    description: "CTA banner button chunk"
    category: CTA section
  - name: cta_banner_button_2_chunk
    description: "CTA banner secondary button chunk"
    category: CTA section
  - name: breadcrumb_chunk
    description: "Breadcrumb chunk"
    category: Breadcrumb
  - name: sidebar_navigation
    description: "Sidebar navigation"
    category: Sidebar
  - name: cta_section
    description: "CTA section"
    category: CTA section
  - name: contact_form_inner
    description: "Contact form inner"
    category: contact
  - name: contact_email_tpl
    description: "Contact email template"
    category: contact
  - name: contact_email_reply_tpl
    description: "Contact email reply template"
    category: contact
  - name: request_form_inner
    description: "Request form inner"
    category: request
  - name: request_email_tpl
    description: "Request email template"
    category: request
  - name: request_email_reply_tpl
    description: "Request email reply template"
    category: request
  - name: social_media_icons_dark
    description: "Social media icons on dark background"
    category: social
  - name: schema_org
    description: "Schema.org markup"
    category: social
  - name: bento_section_1_image_tpl
    description: "Bento section 1 image template"
    category: Bento section
  - name: bento_section_2_image_tpl
    description: "Bento section 2 image template"
    category: Bento section
  - name: bento_section_3_image_tpl
    description: "Bento section 3 image template"
    category: Bento section
  - name: bento_section_4_image_tpl
    description: "Bento section 4 image template"
    category: Bento section
  - name: bento_section_5_image_tpl
    description: "Bento section 5 image template"
    category: Bento section
tvs:
  - name: blog_image
    file: blog_image.tpl
    caption: "Blog Image"
    type: imageplus
    description: "Main image for blog posts"
    category: blog
    inputOptions:
      {
        "targetWidth": "1200",
        "targetRatio": "1.77777777777",
        "allowAltTag": "false",
      }
    templates:
      - "blog-detail-page"
      - "blog-overview-page"
  - name: home_image
    file: home_image.tpl
    caption: "Home Image"
    type: imageplus
    description: "Main image for homepage"
    category: Hero section
    inputOptions: { "targetRatio": "1.77777777777", "allowAltTag": "false" }
    templates:
      - "homepage"
  - name: cta_link
    file: cta_link.tpl
    caption: "CTA Link"
    type: textfield
    description: "Call to action link URL"
    category: blog
    templates:
      - "blog-detail-page"
  - name: cta_text
    file: cta_text.tpl
    caption: "CTA Text"
    type: textfield
    description: "Call to action button text"
    defaultValue: "Read More"
    category: blog
    templates:
      - "blog-detail-page"
  - name: faq_items
    file: faq_items.tpl
    caption: "FAQ Items"
    type: migx
    description: "FAQ questions and answers using MIGX"
    category: faq
    inputOptions:
      formtabs: '{"0":{"caption":"FAQ Item","fields":{"question":{"field":"question","caption":"Question","inputTV":"","inputTVtype":"textfield","configs":"","validation":"","props":""},"answer":{"field":"answer","caption":"Answer","inputTV":"","inputTVtype":"richtext","configs":"","validation":"","props":""}}}}'
      columns: '{"0":{"header":"Question","dataIndex":"question","width":"60%"},"1":{"header":"Answer Preview","dataIndex":"answer","width":"40%","renderer":"this.stripTags"}}'
      btntext: "Add FAQ Item"
      winbuttons: '{"0":{"text":"Save","handler":"this.submit"},"1":{"text":"Cancel","handler":"this.close"}}'
      winwidth: 800
      winheight: 600
      maxitems: 50
    templates:
      - "faq-page"
  - name: home_cta_link
    file: home_cta_link.tpl
    caption: "CTA Link"
    type: calltoactiontv
    description: "Leave link text empty to not display a button."
    category: Hero section
    sortOrder: "3"
    inputOptions:
      values: "@SELECT CONCAT(`pagetitle`, ' (', `id`, ')') AS `name`,`id` FROM `[[+PREFIX]]site_content` WHERE `published` = 1 AND `deleted` = 0 AND `context_key` = '[[+context_key]]'"
      types: "resource||external||tel||mailto"
    templates:
      - "homepage"
  - name: blog_section_title
    file: blog_section_title.tpl
    caption: "Blog section Title"
    type: textfield
    defaultValue: "Dolor sit amet"
    category: Blog section
    sortOrder: "1"
    templates:
      - "homepage"
  - name: blog_section_description
    file: blog_section_description.tpl
    caption: "Blog section Description"
    type: textarea
    defaultValue: "Possimus magnam voluptatum cupiditate veritatis in accusamus quisquam."
    category: Blog section
    sortOrder: "2"
    templates:
      - "homepage"
  - name: bento_section_title
    caption: "Bento section Title"
    type: textfield
    defaultValue: "Voor particulier & zakelijk!"
    category: Bento section
    sortOrder: "1"
    templates:
      - "homepage"
  - name: bento_section_sub_title
    caption: "Bento section Subtitle"
    type: textfield
    defaultValue: "Slimme energieoplossingen van Sunconnect"
    category: Bento section
    sortOrder: "2"
    templates:
      - "homepage"
  - name: bento_section_1_image
    file: bento_section_1_image.tpl
    caption: "Bento 1 Image"
    type: imageplus
    category: Bento section
    sortOrder: "41"
    inputOptions: { "targetRatio": "1.77777777777", "allowAltTag": "false" }
    templates:
      - "homepage"
  - name: bento_section_1_title
    caption: "Bento 1 Title"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "41"
    templates:
      - "homepage"
  - name: bento_section_1_sub_title
    caption: "Bento 1 Subtitle"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "42"
    templates:
      - "homepage"
  - name: bento_section_1_content
    caption: "Bento 1 Content"
    type: textarea
    defaultValue: ""
    category: Bento section
    sortOrder: "43"
    templates:
      - "homepage"
  - name: bento_section_1_cta_link
    file: bento_section_1_cta_link.tpl
    caption: "Bento 1 CTA Link"
    type: calltoactiontv
    description: "Leave link text empty to not display a button."
    category: Bento section
    sortOrder: "44"
    inputOptions:
      values: "@SELECT CONCAT(`pagetitle`, ' (', `id`, ')') AS `name`,`id` FROM `[[+PREFIX]]site_content` WHERE `published` = 1 AND `deleted` = 0 AND `context_key` = '[[+context_key]]'"
      types: "resource||external||tel||mailto"
    templates:
      - "homepage"
  - name: bento_section_2_image
    file: bento_section_2_image.tpl
    caption: "Bento 2 Image"
    type: imageplus
    category: Bento section
    sortOrder: "45"
    inputOptions: { "targetRatio": "1.77777777777", "allowAltTag": "false" }
    templates:
      - "homepage"
  - name: bento_section_2_title
    caption: "Bento 2 Title"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "45"
    templates:
      - "homepage"
  - name: bento_section_2_sub_title
    caption: "Bento 2 Subtitle"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "46"
    templates:
      - "homepage"
  - name: bento_section_2_content
    caption: "Bento 2 Content"
    type: textarea
    defaultValue: ""
    category: Bento section
    sortOrder: "47"
    templates:
      - "homepage"
  - name: bento_section_2_cta_link
    file: bento_section_2_cta_link.tpl
    caption: "Bento 2 CTA Link"
    type: calltoactiontv
    description: "Leave link text empty to not display a button."
    category: Bento section
    sortOrder: "48"
    inputOptions:
      values: "@SELECT CONCAT(`pagetitle`, ' (', `id`, ')') AS `name`,`id` FROM `[[+PREFIX]]site_content` WHERE `published` = 1 AND `deleted` = 0 AND `context_key` = '[[+context_key]]'"
      types: "resource||external||tel||mailto"
    templates:
      - "homepage"
  - name: bento_section_3_image
    file: bento_section_3_image.tpl
    caption: "Bento 3 Image"
    type: imageplus
    category: Bento section
    sortOrder: "49"
    inputOptions: { "targetRatio": "1.77777777777", "allowAltTag": "false" }
    templates:
      - "homepage"
  - name: bento_section_3_title
    caption: "Bento 3 Title"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "49"
    templates:
      - "homepage"
  - name: bento_section_3_sub_title
    caption: "Bento 3 Subtitle"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "50"
    templates:
      - "homepage"
  - name: bento_section_3_content
    caption: "Bento 3 Content"
    type: textarea
    defaultValue: ""
    category: Bento section
    sortOrder: "51"
    templates:
      - "homepage"
  - name: bento_section_3_cta_link
    file: bento_section_3_cta_link.tpl
    caption: "Bento 3 CTA Link"
    type: calltoactiontv
    description: "Leave link text empty to not display a button."
    category: Bento section
    sortOrder: "52"
    inputOptions:
      values: "@SELECT CONCAT(`pagetitle`, ' (', `id`, ')') AS `name`,`id` FROM `[[+PREFIX]]site_content` WHERE `published` = 1 AND `deleted` = 0 AND `context_key` = '[[+context_key]]'"
      types: "resource||external||tel||mailto"
    templates:
      - "homepage"
  - name: bento_section_4_image
    file: bento_section_4_image.tpl
    caption: "Bento 4 Image"
    type: imageplus
    category: Bento section
    sortOrder: "53"
    inputOptions: { "targetRatio": "1.77777777777", "allowAltTag": "false" }
    templates:
      - "homepage"
  - name: bento_section_4_title
    caption: "Bento 4 Title"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "53"
    templates:
      - "homepage"
  - name: bento_section_4_sub_title
    caption: "Bento 4 Subtitle"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "54"
    templates:
      - "homepage"
  - name: bento_section_4_content
    caption: "Bento 4 Content"
    type: textarea
    defaultValue: ""
    category: Bento section
    sortOrder: "55"
    templates:
      - "homepage"
  - name: bento_section_4_cta_link
    file: bento_section_4_cta_link.tpl
    caption: "Bento 4 CTA Link"
    type: calltoactiontv
    description: "Leave link text empty to not display a button."
    category: Bento section
    sortOrder: "56"
    inputOptions:
      values: "@SELECT CONCAT(`pagetitle`, ' (', `id`, ')') AS `name`,`id` FROM `[[+PREFIX]]site_content` WHERE `published` = 1 AND `deleted` = 0 AND `context_key` = '[[+context_key]]'"
      types: "resource||external||tel||mailto"
    templates:
      - "homepage"
  - name: bento_section_5_image
    file: bento_section_5_image.tpl
    caption: "Bento 5 Image"
    type: imageplus
    category: Bento section
    sortOrder: "57"
    inputOptions: { "targetRatio": "1.77777777777", "allowAltTag": "false" }
    templates:
      - "homepage"
  - name: bento_section_5_title
    caption: "Bento 5 Title"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "57"
    templates:
      - "homepage"
  - name: bento_section_5_sub_title
    caption: "Bento 5 Subtitle"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "58"
    templates:
      - "homepage"
  - name: bento_section_5_content
    caption: "Bento 5 Content"
    type: textarea
    defaultValue: ""
    category: Bento section
    sortOrder: "59"
    templates:
      - "homepage"
  - name: bento_section_5_cta_link
    file: bento_section_5_cta_link.tpl
    caption: "Bento 5 CTA Link"
    type: calltoactiontv
    description: "Leave link text empty to not display a button."
    category: Bento section
    sortOrder: "60"
    inputOptions:
      values: "@SELECT CONCAT(`pagetitle`, ' (', `id`, ')') AS `name`,`id` FROM `[[+PREFIX]]site_content` WHERE `published` = 1 AND `deleted` = 0 AND `context_key` = '[[+context_key]]'"
      types: "resource||external||tel||mailto"
    templates:
      - "homepage"
  - name: bento_section_1_title
    caption: "Bento 1 Title"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "41"
    templates:
      - "homepage"
  - name: bento_section_1_sub_title
    caption: "Bento 1 Subtitle"
    type: textfield
    defaultValue: ""
    category: Bento section
    sortOrder: "42"
    templates:
      - "homepage"
  - name: bento_section_1_content
    caption: "Bento 1 Content"
    type: textarea
    defaultValue: ""
    category: Bento section
    sortOrder: "43"
    templates:
      - "homepage"
  - name: bento_section_1_cta_link
    file: bento_section_1_cta_link.tpl
    caption: "Bento 1 CTA Link"
    type: calltoactiontv
    description: "Leave link text empty to not display a button."
    category: Bento section
    sortOrder: "44"
    inputOptions:
      values: "@SELECT CONCAT(`pagetitle`, ' (', `id`, ')') AS `name`,`id` FROM `[[+PREFIX]]site_content` WHERE `published` = 1 AND `deleted` = 0 AND `context_key` = '[[+context_key]]'"
      types: "resource||external||tel||mailto"
    templates:
      - "homepage"
  - name: cta_banner_show
    file: cta_banner_show.tpl
    caption: "Display CTA Banner"
    type: option
    defaultValue: "no"
    inputOptionValues: "yes||no"
    category: CTA section
    sortOrder: "0"
    templates:
      - "homepage"
      - "basic-page"
      - "overview-page"
      - "content-block-page"
  - name: cta_banner_title
    file: cta_banner_title.tpl
    caption: "CTA Banner Title"
    type: textfield
    defaultValue: "Vraag geheel vrijblijvend een offerte aan!"
    category: CTA section
    sortOrder: "1"
    templates:
      - "homepage"
      - "basic-page"
      - "overview-page"
      - "content-block-page"
  - name: cta_banner_description
    file: cta_banner_description.tpl
    caption: "CTA Banner Description"
    type: textarea
    defaultValue: "Heeft u interesse in duurzame energiesystemen? Dan bent u bij ons aan aan het juiste adres!"
    category: CTA section
    sortOrder: "2"
    templates:
      - "homepage"
      - "basic-page"
      - "overview-page"
      - "content-block-page"
  - name: cta_banner_button_1
    file: cta_banner_button_1.tpl
    caption: "CTA Banner Button 1"
    type: calltoactiontv
    description: "Leave link text empty to not display a button."
    category: CTA section
    sortOrder: "3"
    inputOptions:
      values: "@SELECT CONCAT(`pagetitle`, ' (', `id`, ')') AS `name`,`id` FROM `[[+PREFIX]]site_content` WHERE `published` = 1 AND `deleted` = 0 AND `context_key` = '[[+context_key]]'"
      types: "resource||external||tel||mailto"
    templates:
      - "homepage"
      - "basic-page"
      - "overview-page"
      - "content-block-page"
  - name: cta_banner_button_2
    file: cta_banner_button_2.tpl
    caption: "CTA Banner Button 2"
    type: calltoactiontv
    description: "Leave link text empty to not display a button."
    category: CTA section
    sortOrder: "4"
    inputOptions:
      values: "@SELECT CONCAT(`pagetitle`, ' (', `id`, ')') AS `name`,`id` FROM `[[+PREFIX]]site_content` WHERE `published` = 1 AND `deleted` = 0 AND `context_key` = '[[+context_key]]'"
      types: "resource||external||tel||mailto"
    templates:
      - "homepage"
      - "basic-page"
      - "overview-page"
      - "content-block-page"
  - name: showbreadcrumb
    file: showbreadcrumb.tpl
    caption: "Display breadcrumb"
    type: option
    defaultValue: "yes"
    inputOptionValues: "yes||no"
    category: Page options
    sortOrder: "1"
    templates:
      - "overview-page"
      - "basic-page"
  - name: showsidebar
    file: showsidebar.tpl
    caption: "Display sidebar"
    type: option
    defaultValue: "no"
    inputOptionValues: "yes||no"
    category: Page options
    sortOrder: "2"
    templates:
      - "overview-page"
      - "basic-page"
systemSettings:
  - key: theme_dir.custom_path
    value: "{{pkg_assets_path}}/"
  - key: theme_dir.custom_url
    value: "{{pkg_assets_path}}/"

2 Likes