Skip to content

Comments

[WC-3105]: Mendix Pluggable MCP#2035

Open
rahmanunver wants to merge 11 commits intomainfrom
mendix-pluggable-mcp
Open

[WC-3105]: Mendix Pluggable MCP#2035
rahmanunver wants to merge 11 commits intomainfrom
mendix-pluggable-mcp

Conversation

@rahmanunver
Copy link
Contributor

@rahmanunver rahmanunver commented Jan 20, 2026

Pull request type

New feature (non-breaking change which adds functionality)


Description

@mendix/pluggable-widgets-mcp — MCP Widget Generator

What this is

An MCP server that lets any AI assistant scaffold, configure, and build a
Mendix pluggable widget — following our conventions, using our tooling,
producing output that meets our quality bar.

You describe the widget. The server handles the rest: scaffolding via
@mendix/generator-widget, property configuration, code generation,
linting, builds. The output is a deployable .mpk.

The premise: the web-content team's domain knowledge should be accessible
to anyone building a widget, not just people who already know our stack.

How it works

The server registers 7 tools and 2 guideline resources over MCP. An AI
client connects, picks up the tools and resources, and can drive a full
widget development loop.

Tools:

Tool Purpose
create-widget Scaffold a new widget via @mendix/generator-widget
generate-widget-code Generate XML + TSX + SCSS from a property
definition
update-widget-properties Incrementally add, remove, or modify
properties
build-widget Run npm run build, produce an .mpk
list-widget-files List widget directory contents
read-widget-file Read a file from a widget directory
write-widget-file Write files (single or batch)

Resources (injected into the LLM's context on demand):

  • mendix://guidelines/property-types — every widget property type with
    JSON schema and usage notes
  • mendix://guidelines/widget-patterns — reusable TSX/SCSS patterns for
    common widget types

The LLM provides JSON property definitions → XML is generated
deterministically by our generators. The LLM never needs to reason about
our XML schema.

A typical session

Starting from a single prompt like "Build a clickable counter widget that
increments on every click"
, a capable LLM will:

  1. Call create-widget to scaffold the initial project
  2. Call generate-widget-code with the right property definitions
  3. Call write-widget-file to implement the component logic
  4. Call build-widget to compile and produce the .mpk

Other details

  • Transport: STDIO or HTTP (port 3100) — works with any MCP-compatible
    client
  • Output: Widgets land in generations/ by default; configurable via
    MCP_ALLOWED_OUTPUT_PATHS
  • Security: Path traversal blocked, extension whitelist enforced, build
    paths sandboxed
  • Progress notifications go to client UI indicators, not the
    conversation — this is per MCP spec, not a bug

What should be covered while testing?

cd packages/pluggable-widgets-mcp
pnpm install && pnpm build

Link globally for use with any MCP client:
npm link  # Use npm, not pnpm — better compatibility across clients

MCP server config (STDIO):
{
    "mcpServers": {
        "pluggable-widgets-mcp": {
            "type": "stdio",
            "command": "pluggable-widgets-mcp"
        }
    }
}

Prompt to try: "Create a widget called ProgressCircle that shows a
percentage in a circular progress bar"

For step-by-step debugging, use MCP Inspector:
npx @modelcontextprotocol/inspector node dist/index.js stdio

Explicit test flow

1. "Create a widget called ColorPicker with a color attribute property"
2. "List the files in the ColorPicker widget"
3. "Read the ColorPicker.xml file"
4. "Update the widget to add an onChange action property"
5. "Build the ColorPicker widget"

A capable LLM will drive the full loop from a single description without
needing each step broken out.

See packages/pluggable-widgets-mcp/README.md for full documentation.

@rahmanunver rahmanunver requested a review from a team as a code owner January 20, 2026 08:27
Comment on lines 300 to 310
### entity

Entity selector.

```json
{
"key": "targetEntity",
"type": "entity",
"caption": "Target entity"
}
```
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type doesn't exist

Comment on lines 276 to 281
```xml
<property key="dataSource" type="datasource" isList="true" required="false">
<caption>Data source</caption>
<description>Source of items to display</description>
</property>
```
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to let it know about XML? If we have high level definition of how properties look like, we might just treat XML as black box, so LLM doesn't need to dive into it. Let's discuss this.

Comment on lines +480 to +484
```json
{
"systemProperties": ["Name", "TabIndex", "Visibility"]
}
```
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can define every system property as a standalone property of particular shape, similar to other properties.

Something like:

{
    type: "system",
    name: "TabIndex"
}

Comment on lines 494 to 502
```xml
<propertyGroup caption="Common">
<systemProperty key="Name" />
<systemProperty key="TabIndex" />
</propertyGroup>
<propertyGroup caption="Visibility">
<systemProperty key="Visibility" />
</propertyGroup>
```
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Property groups have to be defined separately, they are not autogenerated. But we probably don't want to expose XML if we see it as implementation details.


---

## Full Widget Definition Example
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not clear how Property Groups are defined, are they part of this JSON, or supplied in a different was somehow?

Comment on lines 46 to 47
type WriteWidgetFileInput = z.infer<typeof writeWidgetFileSchema>;
type BatchWriteWidgetFilesInput = z.infer<typeof batchWriteWidgetFilesSchema>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are two ways for doing essentially the same, can this confuse LLM more that it helps?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this file is empty

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we use this? I see currently LLM is able to read XML file directly, I didn't find where it communicates properties in json format.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants