appsmith-mcp
appsmith-mcp
MCP server for a self-hosted Appsmith instance, over Appsmith's own REST API — the same API the editor uses.
Built because no reliable Appsmith MCP server exists: there is nothing published on
npm or PyPI, nothing in the appsmithorg GitHub org, and the listings that surface
in search results are auto-generated stubs with no implementation behind them.
How it authenticates
Appsmith uses Spring Security form login behind CSRF protection:
A
GET /api/v1/users/meseeds anXSRF-TOKENcookie.POST /api/v1/loginsendsusername/passwordform-encoded, echoing that token in theX-XSRF-TOKENheader.The response sets a
SESSIONcookie used for every later call.
The client logs in lazily on first use and re-logs in once automatically on a 401,
so an expired session heals itself instead of failing a tool call.
Setup
npm install
cp .env.example .env # then put your Appsmith password in .env
npm run check # read-only checks against the live instance
npm run check -- --scratch # also covers writes, via a throwaway app it deletesnpm run check prints a PASS/FAIL row per endpoint — run it whenever an Appsmith
upgrade might have moved routes. --scratch creates an mcp-smoke-* application,
exercises create-page / publish / delete against it, and removes it again.
Registration
Registered at user scope, so it is available in every project rather than one:
claude mcp add appsmith --scope user -- \
node --env-file-if-exists=<abs-path>/.env <abs-path>/src/index.jsThat writes to ~/.claude.json. Paths must be absolute — a user-scope server is
launched from whatever directory the session happens to be in.
Credentials are read from .env via Node's --env-file-if-exists, so no secret
lives in the MCP config. Because .env pins one instance, retarget it by changing
APPSMITH_URL.
To scope it back to a single project, claude mcp remove appsmith --scope user and
put the same command in that project's .mcp.json.
Tools
Read
Tool | Endpoint |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Edit — changing an existing app's logic
Tool | Endpoint |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Write — structure and lifecycle
Tool | Endpoint |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| any route; non-GET gated |
Editing is deliberately typed rather than left to appsmith_api_request: the raw tool
is gated behind ALLOW_DESTRUCTIVE and cannot be safely auto-approved, since it can
reach any endpoint. The typed tools each do one thing, so they can be allow-listed
individually.
appsmith_update_query sends only the fields you pass — the server merges the rest,
so the datasource and untouched settings survive. It deliberately does not rename:
renaming needs a refactor pass that rewrites bindings across the app, and a plain
PUT would leave every reference broken.
Adding widgets
There is deliberately no "create widget from scratch" tool. Appsmith's widget defaults
live in its frontend, not its API — /api/v1/widgets, /widget-config and
/configs all 404 — and a real widget carries 24–65 properties depending on type. A
hand-written defaults catalogue would replicate frontend logic, drift on every Appsmith
upgrade, and fail silently when it did. The two supported routes instead:
appsmith_clone_widgetcopies a widget that Appsmith itself created, so the defaults are always correct and there is nothing to maintain. Every descendant gets a freshwidgetIdand a free name (Canvas1→Canvas2,interno→interno1); names only need to be unique per page. The copy is placed below its siblings so it cannot land on another widget. Bindings inside the copy are not rewritten — one that referenced the source by name still points at the original, so the response returnsrenamedInsideCopyfor the caller to fix.appsmith_generate_crud_pagecalls Appsmith's own generator for a whole screen.
Bindings
appsmith_update_widget maintains dynamicBindingPathList for you. Appsmith only
evaluates {{ }} on properties listed there and the server does not infer it: a
binding written without registration is stored verbatim and rendered as a literal
string — which, for a boolean like isDisabled, is always truthy. Setting a binding
adds the path; setting a literal removes it. A widget's other properties are preserved,
since the whole layout is read, patched and written back.
Safety
appsmith_delete_applicationand non-GETappsmith_api_requestcalls refuse to run unlessAPPSMITH_MCP_ALLOW_DESTRUCTIVE=true.appsmith_execute_queryruns against the real datasource — a write query writes. It is not gated, since running queries is the point, so check what a query does before running it.Large payloads (page DSLs, exports) are truncated at
APPSMITH_MCP_MAX_CHARS.appsmith_get_pagereturns a widget outline by default; passfull=truefor the raw layout.appsmith_export_applicationtakes anoutputPathto write to disk instead of returning inline.
Instance quirks found while building this
Two behaviours differ from what the CE controllers suggest, both handled in the code:
GET /applications/homedocumentsworkspaceIdas optional, but this instance rejects the call without it.appsmith_list_applicationsfans out across every workspace when no id is given.POST /pagesrejects an emptylayoutsarray, andlayouts: [{}]silently creates a page whose DSL isnull— blank and unusable in the editor. The rootCANVAS_WIDGETcanvas must be sent explicitly; seesrc/defaults.js.
Maintenance
Routes were taken from the Appsmith backend controllers, not guessed:
controllers/ce/ApplicationControllerCE.javacontrollers/ce/PageControllerCE.javacontrollers/ce/ActionControllerCE.javacontrollers/ce/DatasourceControllerCE.javacontrollers/ce/WorkspaceControllerCE.javaconstants/ce/UrlCE.java
Appsmith's API is not a documented public contract, so a major upgrade can move a
route. npm run check is the fast way to find out.
License
MIT — see LICENSE.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Davidgraciano/appsmith-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server