mendix-mcp-server
Enables AI agents to read and modify Mendix application models through MCP tools for creating modules, entities, pages, microflows, deploying, and querying runtime data.
README
Mendix MCP Server
An MCP (Model Context Protocol) server that exposes the Mendix Model SDK, Mendix Platform API, Deploy API, and a running app's runtime OQL endpoint as a suite of tools an AI agent (e.g. Claude Code) can call.
In short: it lets an LLM read and modify a Mendix application model — create modules, entities, attributes, associations, pages, microflows, REST services, security rules, navigation, and enumerations — and then commit, deploy, or query the result, all through plain tool calls over HTTP.
Table of contents
- How it works
- Architecture
- The four Mendix surfaces it talks to
- The working-copy lifecycle (the core pattern)
- Request lifecycle
- Setup
- Configuration
- Running the server
- Connecting an MCP client
- Tool catalog
- Project layout
- How a typical task flows end-to-end
- Notes, limitations & gotchas
How it works
Mendix apps are not edited as loose files. The model lives on Mendix's Team Server, and
the canonical way to change it programmatically is the Mendix Model SDK (mendixmodelsdk)
driven through the Platform SDK (mendixplatformsdk). The pattern is always:
- Open a temporary online working copy of an app from a branch.
- Open its model (a live, typed, in-memory object graph of the whole app).
- Read or mutate model elements (entities, pages, microflows, …).
- Flush pending changes and commit the working copy back to the branch.
This server wraps that pattern in ~60 discrete MCP tools. Each tool is a thin, focused operation ("add an attribute to an entity", "add a widget to a page", "create a REST operation") that:
- takes an
app_id(and usually abranch, defaulting tomain), - opens a fresh working copy + model,
- performs one well-scoped change,
- commits with a generated message,
- and returns a human/JSON result.
The AI agent orchestrates these small tools into larger changes. The server itself stays stateless — it holds no session between calls.
Architecture
┌───────────────────┐ JSON-RPC over HTTP (POST /mcp) ┌────────────────────────┐
│ MCP client │ ───────────────────────────────────────► │ Mendix MCP Server │
│ (Claude Code, etc.)│ ◄─────────────────────────────────────── │ (Node.js, port 3001) │
└───────────────────┘ tool results / streamed events └───────────┬────────────┘
│
┌──────────────────────────────────────────────────────┼───────────────────────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────┐ ┌────────────────────┐
│ Platform SDK │ │ Deploy API (v2) │ │ Runtime OQL endpoint │ │ Local FS / Web │
│ + Model SDK │ │ deploy.mendix.com │ │ /rest/oql/v1/query │ │ (glob/read/write/ │
│ (Team Server model)│ │ │ │ on the running app │ │ fetch/docs search)│
└────────────────────┘ └──────────────────────────┘ └──────────────────────┘ └────────────────────┘
▲ ▲ ▲
MENDIX_PAT MENDIX_USERNAME + MENDIX_RUNTIME_TOKEN
MENDIX_API_KEY
Two source files form the spine:
src/server.ts— the HTTP + MCP transport layer. Builds anMcpServer, registers every tool group, and serves them over a Streamable HTTP transport atPOST /mcp.src/mendix-client.ts— the shared Mendix helper layer: authentication, working-copy/model lifecycle, module/document lookup, multi-language text handling, and result formatting.
Everything else lives under src/tools/, one file per capability area, each
exporting a registerXxxTools(server) function that server.ts calls.
The four Mendix surfaces it talks to
The server does not hit a single Mendix API. Different tools use different Mendix surfaces, each with its own credential:
| Surface | Used by | Credential | What it does |
|---|---|---|---|
| Model SDK / Platform SDK | Most tools — domain, pages, flows, security, navigation, enums, REST modeling, ped_*, project_commit/branch/export/settings |
MENDIX_PAT |
Opens working copies and reads/mutates the app model on the Team Server. |
Deploy API v2 (deploy.mendix.com) |
project_deploy, project_get_deploy_status |
MENDIX_USERNAME + MENDIX_API_KEY |
Triggers cloud deployments and reports their status. |
Runtime OQL endpoint (/rest/oql/v1/query) |
oql_read |
MENDIX_RUNTIME_TOKEN |
Runs OQL against a live, running app instance and returns data. |
| Local FS / public web | glob, read_file, write_file, read_skill, web_fetch, search_mendix_knowledge_base, oql_generate |
none | Local file ops, doc search, and pure string generation — no Mendix auth needed. |
The working-copy lifecycle (the core pattern)
Almost every model-mutating tool follows the same shape, implemented via helpers in
src/mendix-client.ts:
// 1. Open a fresh working copy for the app/branch and open its model.
const wc = await getWorkingCopy(app_id, branch); // createTemporaryWorkingCopy(branch)
const model = await wc.openModel();
// 2. Locate the target element.
const mod = findModule(model, moduleName); // by name
const doc = findDocument(model, moduleName, docName); // recursive folder search
// 3. Mutate the in-memory model graph (create/update/delete SDK objects).
// ...
// 4. Flush pending changes and commit the working copy back to the branch.
await commitWC(wc, model, "descriptive commit message", branch);
Read-only tools skip step 4 and just call getModel(app_id, branch) to open a throwaway
working copy for inspection.
Key helpers in mendix-client.ts:
mendix— the singletonMendixPlatformClient. Its PAT is bridged fromMENDIX_PATinto the SDK's expectedMENDIX_TOKENmechanism viasetPlatformConfig(...).getWorkingCopy(appId, branch)/getModel(appId, branch)— open a working copy / model.commitWC(wc, model, message, branch)—flushChanges()thencommitToRepository(...).findModule/findDocument/splitQualifiedName— resolveModule.Elementnames, searching recursively through folders.setText/readText/getProjectLanguageCodes— Mendix user-visible text is never a plain string; it's aTextholding oneTranslationper configured language.setTextwrites a translation for every project language (avoiding consistency errors) and caches the language list per model.ok(msg)/json(data)/errorResult(err)— normalize tool return payloads.safe(handler)— wraps every tool handler so a thrown error becomes an MCP error result (isError: true) instead of crashing the server.
Request lifecycle
The HTTP layer in src/server.ts runs in stateless mode:
- A client POSTs a JSON-RPC MCP message to
http://localhost:3001/mcp. - The body is read and parsed.
- A fresh
McpServer+StreamableHTTPServerTransportpair is built per request. (An MCP server/transport pair can only beconnect()-ed once — reusing it throws on the second call — so the server builds a new pair each time rather than holding a session.) - All tool groups are registered on that fresh server, it connects to the transport, and the transport handles the request/response.
- On connection close, both transport and server are closed.
Any non-/mcp path returns 404. Errors are returned as JSON-RPC error objects
(code: -32603).
Setup
Prerequisites
- Node.js (with support for ES modules /
NodeNext; Node 18+ recommended for globalfetch). - A Mendix account with:
- a Personal Access Token (PAT) with
mx:modelrepositoryscopes (for model editing), - optionally an API key + username (for deployments),
- optionally a runtime token (for live OQL queries).
- a Personal Access Token (PAT) with
- The Mendix app's App ID (found in the Mendix Portal / Developer Portal).
Install
npm install
Configuration
Copy .env.example to .env and fill in your credentials:
MENDIX_PAT=your_personal_access_token # Model/Platform SDK — model editing
MENDIX_USERNAME=your@email.com # Deploy API — deployments
MENDIX_API_KEY=your_deploy_api_key # Deploy API — deployments
MENDIX_RUNTIME_TOKEN=your_runtime_token # Runtime OQL — live data queries
PORT=3001 # HTTP port (default 3001)
.env is loaded automatically at startup via dotenv/config. Each credential is only
required for the tools that use it — you can run model-editing tools with just MENDIX_PAT.
Running the server
# Type-check + compile TypeScript → dist/
npm run build
# Run the compiled server
npm start
# → Mendix MCP -> http://localhost:3001/mcp
# Or run directly from source during development (ts-node loader):
npm run dev
# Auto-restart on file changes:
npm run watch
Connecting an MCP client
Point any MCP client at the HTTP endpoint. For Claude Code, add an HTTP MCP server:
claude mcp add --transport http mendix http://localhost:3001/mcp
Or via a client config that supports Streamable HTTP transports, use the URL
http://localhost:3001/mcp. Once connected, the client discovers all tools below and the
agent can call them by name.
Tool catalog
All tools that touch the model accept app_id and (usually) an optional branch (default
main). Grouped by source file:
Filesystem & knowledge — filesystem.ts, knowledge.ts
| Tool | Description |
|---|---|
glob |
Find files matching a glob pattern. |
read_file |
Read a file's contents. |
write_file |
Write or overwrite a file. |
read_skill |
Read a skill/snippet from the skills library. |
web_fetch |
Fetch content from a URL. |
search_mendix_knowledge_base |
Search Mendix docs / knowledge base (docs.mendix.com). |
Platform / element document ops (ped_*) — ped.ts
Generic, low-level building blocks used by the higher-level tools.
| Tool | Description |
|---|---|
ped_read_document |
Read a document's full JSON representation. |
ped_get_schema |
Summarize the SDK class + common properties for a document type. |
ped_create_document |
Create a Page, Microflow, Nanoflow, Enumeration, or Snippet. |
ped_create_module |
Create a new module. |
ped_update_document |
Apply key-value property updates to a document. |
ped_check_errors |
Run consistency checks on the model. |
ped_find_document |
Search documents by name substring / module / type. |
ped_list_folder |
List documents directly inside a module (or subfolder). |
Domain model — domain.ts
| Tool | Description |
|---|---|
entity_create |
Create an entity (persistence via generalization; see note below). |
entity_add_attribute |
Add an attribute (optional default value + required rule). |
entity_update_attribute |
Apply property updates to an attribute. |
entity_delete_attribute |
Delete an attribute. |
entity_list_attributes |
List an entity's attributes with names + types. |
entity_set_access_rules |
Set entity access rules. |
entity_copy |
Copy an entity (including attributes). |
entity_delete |
Delete an entity. |
association_create |
Create an association between two entities (cross-module OK). |
association_set_properties |
Apply property updates to an association. |
domain_model_export |
Export a curated summary of a module's domain model. |
Supported attribute types: String, Integer, Long, Decimal, Boolean, DateTime, Enum, AutoNumber, Binary, HashString.
Enumerations, constants & snippets — enums.ts
| Tool | Description |
|---|---|
enum_add_value / enum_update_value / enum_list_values |
Manage enumeration values. |
constant_create / constant_set_value |
Create/set a Constant document. |
snippet_create / snippet_add_to_page |
Create a reusable snippet and place it on a page. |
Pages & widgets — pages.ts
| Tool | Description |
|---|---|
page_add_widget / page_remove_widget / page_move_widget |
Add/remove/reposition widgets. |
page_set_widget_property |
Set a single widget property (dynamic key-value). |
page_set_data_source |
Wire a widget's data source. |
page_set_conditional_visibility |
Set a visibility expression on a widget. |
page_set_layout |
Change the page's layout. |
page_add_listview |
Add a ListView wired to an entity. |
page_add_datagrid |
Add a DataGrid wired to an entity, one column per attribute. |
page_set_class |
Set a widget's CSS class. |
page_duplicate |
Duplicate a page. |
page_set_navigation_target |
Make an ActionButton navigate to another page. |
page_set_button_caption |
Set a button's caption. |
Microflows / nanoflows — flows.ts
| Tool | Description |
|---|---|
flow_add_activity |
Add an activity node. |
flow_add_decision |
Add an exclusive split with an expression condition. |
flow_connect_activities |
Connect nodes with flows. |
flow_configure_activity |
Configure an activity's properties. |
flow_add_variable |
Add a create-variable activity. |
flow_add_retrieve |
Add a database retrieve (list by entity). |
flow_add_commit |
Add a commit-object activity. |
flow_add_loop |
Add a loop over a list variable (empty body). |
flow_add_error_handler |
Add an error handler. |
flow_call_microflow |
Call another microflow with argument mappings. |
flow_call_rest_service |
Add a call-REST-service activity. |
flow_get_activities |
List all nodes (id, type, position). |
flow_run_microflow |
Run a microflow. |
REST services — rest.ts
| Tool | Description |
|---|---|
rest_create_consumed_service / rest_add_operation |
Model a consumed REST service + operations. |
rest_create_published_service / rest_add_published_resource |
Model a published REST service at a base path. |
rest_import_openapi |
Import an OpenAPI/Swagger spec (pragmatic, non-$ref parser) into a consumed service. |
rest_list_services |
List all consumed + published services. |
rest_set_mapping |
Configure a mapping. |
rest_test_operation |
Invoke an operation for testing. |
Security — security.ts
| Tool | Description |
|---|---|
security_create_userrole / security_list_roles |
Manage project user roles. |
security_set_page_access |
Set module roles allowed on a page. |
security_set_microflow_access |
Set module roles allowed on a microflow. |
security_set_entity_access |
Set entity access rules. |
security_get_access_summary |
Summarize access configuration. |
Navigation — navigation.ts
| Tool | Description |
|---|---|
navigation_get_menu |
Read a navigation profile's menu. |
navigation_add_menu_item / navigation_remove_menu_item |
Add/remove menu items. |
navigation_reorder_items |
Reorder menu items. |
navigation_set_home_page |
Set the profile's home page. |
OQL — oql.ts
| Tool | Description | Auth |
|---|---|---|
oql_generate |
Build an OQL string from parameters (local templating). | none |
oql_read |
Run OQL against a live running app (POST /rest/oql/v1/query). |
MENDIX_RUNTIME_TOKEN |
Deployment & project — deployment.ts
| Tool | Description | Auth |
|---|---|---|
project_commit |
Open a working copy and commit (checkpoint history). | MENDIX_PAT |
project_create_branch |
Create a branch. | MENDIX_PAT |
project_get_revisions |
Get a branch's commit history (Team Server API). | MENDIX_PAT |
project_deploy |
Deploy to a cloud environment (Sandbox/Test/Acceptance/Production). | MENDIX_USERNAME + MENDIX_API_KEY |
project_get_deploy_status |
Get a deployment's status. | MENDIX_USERNAME + MENDIX_API_KEY |
project_export_mpk |
Export the model as a local .mpk. |
MENDIX_PAT |
project_get_settings |
Get the app's project settings document. | MENDIX_PAT |
Project layout
mendix-mcp-server/
├── src/
│ ├── server.ts # HTTP + MCP transport; registers all tool groups
│ ├── mendix-client.ts # Auth, working-copy/model lifecycle, helpers
│ └── tools/
│ ├── filesystem.ts # glob / read / write / fetch / read_skill
│ ├── knowledge.ts # Mendix docs search
│ ├── ped.ts # generic document/module ops (create/read/update/errors)
│ ├── domain.ts # entities, attributes, associations
│ ├── enums.ts # enumerations, constants, snippets
│ ├── pages.ts # pages & widgets
│ ├── flows.ts # microflows / nanoflows
│ ├── rest.ts # consumed & published REST services, OpenAPI import
│ ├── security.ts # user roles, page/microflow/entity access
│ ├── navigation.ts # navigation menus
│ ├── oql.ts # OQL generate + live read
│ └── deployment.ts # commit, branch, deploy, export, settings
├── dist/ # compiled output (npm run build)
├── package.json
├── tsconfig.json
├── .env / .env.example # credentials & port
└── README.md
How a typical task flows end-to-end
Say the agent is asked: "Add a Customer entity to the Sales module with a Name field,
put it on a data grid, and deploy to Sandbox."
entity_create→ opens a working copy of the app, creates theCustomerentity inSales, commits.entity_add_attribute→ opens a fresh working copy, addsName(String), commits.ped_create_document→ creates aCustomerOverviewPage.page_add_datagrid→ wires a DataGrid toSales.Customerwith a column per attribute, commits.navigation_add_menu_item→ links the page into the navigation menu.ped_check_errors→ runs consistency checks to confirm the model is valid.project_deploy→ hits the Deploy API to push to the Sandbox environment.project_get_deploy_status→ polls until the deployment reports success.
Each step is a separate stateless HTTP call; the agent decides the order and reacts to each tool's result.
Notes, limitations & gotchas
- Stateless by design. No sessions are kept; each request builds and tears down its own MCP server/transport pair. This is intentional — the MCP SDK forbids reusing a connected pair.
- Every mutating call re-opens a working copy. Because tools are independent, a multi-step
change is a series of commits, not one atomic transaction. Use
project_committo checkpoint andped_check_errorsto validate along the way. - Text is multi-language. Any caption/label/title is a
Textwith oneTranslationper project language. Always use thesetText/readTexthelpers (the higher-level tools do this for you) — don't assume a plain string. - Entity persistence is expressed through the entity's generalization, not a standalone
flag: a
NoGeneralizationwithpersistable=true/false, or aGeneralizationpointing at a parent entity (which then determines persistence). rest_import_openapiis pragmatic, not complete. It readsinfo.title, the server URL, andpaths.*.<method>.operationId, but does not resolve$refs or import parameter/response schemas.- Credential scoping. Model tools need only
MENDIX_PAT; deploy tools additionally needMENDIX_USERNAME+MENDIX_API_KEY;oql_readneedsMENDIX_RUNTIME_TOKEN. Missing credentials produce a clear error only when the relevant tool is called. - Errors are non-fatal. The
safe()wrapper converts thrown errors into MCP error results, so a failing tool call returns an error payload rather than taking the server down.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。