Monday MCP
This server is a local MCP server that connects AI assistants to monday.com, translating plain column values into monday.com's API format and also providing browser/desktop automation modes for UI actions without an API token.
Data Operations (via API):
Get current user info (
monday_get_me)List users, workspaces, and boards (
monday_list_users,monday_list_workspaces,monday_list_boards)Get board layout with column types and status/dropdown labels (
monday_get_board)List/search items with filters and readable column values (
monday_list_items)Get full item details with raw JSON (
monday_get_items)Create, update, move, and delete items (
monday_create_item,monday_update_item,monday_move_item,monday_delete_item)Create subitems (
monday_create_subitem)Read and post updates/comments (
monday_list_updates,monday_create_update)Run raw GraphQL queries/mutations (
monday_graphql)
Addressing & UI Automation:
Resolve ids to URLs for browser/desktop navigation (
monday_resolve_location)Open the desktop app at a location via CDP (
monday_open)Perform UI actions like reading item cards and posting updates via DOM (
monday_ui_action)Supports no-token access by riding an already logged-in user session in desktop/web modes
Conversion & Safety Features:
Converts plain values (e.g., "Done" for status, dates, people, tags) to monday.com's expected JSON shapes
Accepts column titles as keys and normalizes case/spacing/hyphens/underscores
Validates status/dropdown labels before sending, optionally creating missing labels
Provides read-only mode, board allow-listing, and requires confirmation for permanent deletion
No telemetry; token stays local and is redacted from logs
Three Modes:
Mode 3 (API): Full data CRUD via GraphQL
Mode 2 (Web app): UI-only actions, screenshots, no token needed, using local Chrome or remote Browser Run
Mode 1 (Desktop app): Drive the desktop app over CDP, no token needed, for desktop-specific tasks
Provides tools for interacting with the monday.com GraphQL API, including listing boards, managing items (create, update, move, delete), handling column values with automatic type translation, posting comments, and managing users and workspaces.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Monday MCPcreate a "Bug" item on the Launch board with Status "Stuck""
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
monday-mcp
A local Model Context Protocol server for monday.com — plus a documented, reverse‑engineered map of three ways an agent can reach a monday.com location without any discovery spend: the GraphQL API, the web app in a browser, and the native desktop app over the Chrome DevTools Protocol.
It runs on your machine and speaks straight to the monday.com GraphQL API. Your token stays on your machine. No relay, no hosted middle layer, no telemetry.
Unofficial. This project is not built by monday.com, and it carries no endorsement from monday.com. "monday.com" is a trademark of monday.com Ltd.
Claude, Cursor, or any MCP client
| stdio
monday-mcp (this server, on your machine)
| HTTPS
api.monday.comContents
Related MCP server: JSON Canvas MCP Server
Why another monday.com server
Most failures with the monday.com API come from one thing: every column type
stores a different JSON shape. A model that writes "Done" into a status
column gets a silent no-op or an error with no clue in it.
This server does the translation. You send a plain value, and the server converts it to the shape monday.com stores.
// what the model sends
{ "Status": "Done", "Due date": "2026-08-14", "Owner": [12345678] }
// what monday.com receives
{
"status": { "index": 1 },
"date_1": { "date": "2026-08-14" },
"person": { "personsAndTeams": [{ "id": 12345678, "kind": "person" }] }
}The server also refuses a bad value before it sends a request, and the refusal names the labels the column accepts:
Column "Status" has no status "Shipped".
It accepts: "Working on it", "Done", "Stuck".
Send create_labels_if_missing true to add it.Three more things this server does:
Column titles work as keys. Use
"Due date"ordate_1. Letter case, spaces, hyphens and underscores do not matter.Status filters translate too. A filter on
"Stuck"becomes the numeric label index that the API needs.Safety rails. A read-only mode, a board allow list, and a permanent delete that needs explicit confirmation.
Three ways to reach monday.com
The API server (Mode 3) is the primary, complete, contract‑backed path. The other two
exist for the roughly ten percent of tasks the API can't do — pure‑UI actions, visual
verification, or working with no API token at all by riding a session you're already
logged into. All three are documented in depth in docs/interface-map.md.
Mode 3 — API | Mode 2 — Web app | Mode 1 — Desktop app | |
Transport |
| Chrome (local) or Cloudflare Browser Run (remote), driven by DOM/CDP | Electron webview over CDP ( |
Needs an API token | Yes | No (rides your login) | No (rides your login) |
Reads/writes any column type | ✅ (with shape translation) | ⚠️ item‑card / canvas‑limited | ⚠️ same |
Address group / column | ✅ | group via DOM, column API‑only | same |
Visual verification | ❌ | ✅ | ✅ |
Works headless / on a server | ✅ | remote only | ❌ |
Best for | all data CRUD | UI‑only actions, screenshots, no‑token | desktop‑specific, no‑token |
Rule of thumb: data → Mode 3; pixels or UI‑only → Mode 2 (local Chrome); desktop‑specific or no token on a workstation → Mode 1; headless with no local Chrome → Mode 2 (Cloudflare Browser Run).
The addressing model
Every mode is a function of one address. Resolve it once, dispatch anywhere:
interface MondayLocator {
account_slug: string; // "acme" -> https://acme.monday.com (required for URL modes)
board_id?: string; // e.g. "9876543210"
view_id?: string; // a board view (tab)
item_id?: string; // a.k.a. pulse id
update_id?: string; // an update/post on an item
asset_id?: string; // a file on an item
doc_id?: string; block_id?: string; // a workdoc / a block in it
workspace_id?: string; dashboard_id?: string;
group_id?: string; // NOT URL-addressable — from the API (or a board's data-group-id)
column_id?: string; // NOT URL-addressable — from the API
}The hard ceiling. Only board / view / item / update (plus ?asset_id= and
?doc_id=&blockId=) are URL‑addressable. Groups and columns have no URL anchor at all —
resolve them through the API before any browser‑mode step that needs them.
Target | URL (under |
Board |
|
Board view |
|
Item (opens the card overlay) |
|
Update on an item |
|
File over an item |
|
Standalone workdoc |
|
Workspace |
|
Public board view (no login) |
|
Mode 3 — the API server (start here)
This is the MCP server. Node 20 or newer.
Install
git clone https://github.com/ashrocket/monday-mcp.git
cd monday-mcp
npm install # this also builds, through the prepare scriptGet an API token
Open monday.com.
Click your avatar at the bottom left.
Choose Developers, then My access tokens, then Show.
Copy the token.
An admin may prefer the account token at Administration > API.
The token carries your own permissions. It sees the boards you see.
Connect it
Claude Code
claude mcp add monday --env MONDAY_API_TOKEN=your-token -- node /full/path/to/monday-mcp/dist/index.jsClaude Desktop, Cursor, and other clients — add this to the MCP server configuration file:
{
"mcpServers": {
"monday": {
"command": "node",
"args": ["/full/path/to/monday-mcp/dist/index.js"],
"env": { "MONDAY_API_TOKEN": "your-token" }
}
}
}To keep the token out of the configuration file, put it in a file and point at the file instead:
{
"mcpServers": {
"monday": {
"command": "node",
"args": [
"/full/path/to/monday-mcp/dist/index.js",
"--token-file",
"~/.config/monday/token"
]
}
}
}The server takes its configuration from the environment that the MCP client
gives it. It does not read a .env file by itself. For local work, use
Node's own flag: node --env-file=.env dist/index.js.
Prove it works
cp .env.example .env # then put your token in .env
npm run smoke # read-only checks against your account
npm run smoke -- --write # adds a create, update, comment and archive cycleThe write cycle archives the item that it makes, so it leaves no clutter.
Tools
Tool | What it does |
| The user behind the token, and the account. Prove the connection. |
| List or search boards. Returns the board ids. |
| Groups, columns, and the labels each status or dropdown accepts. |
| A page of items, filtered, with readable column text. |
| Full detail for up to 100 items, with column ids and stored JSON. |
| Create an item, with plain column values. |
| Change columns, the name, or both. |
| Move an item to another group. |
| Create a subitem, with column values. |
| Archive by default. Permanent delete needs |
| Read the conversation on an item. |
| Post a comment on an item. |
| Find the numeric user id a people column needs. |
| List the workspaces. |
| An escape hatch for anything the other tools miss. |
In read-only mode only the eight read tools register, plus monday_graphql, which
stays but refuses any mutation. If a board allow list is set, monday_graphql is not
registered at all, because a raw document cannot honour the list — so read-only and
an allow list together leave just the eight read tools.
Three more tools address and drive the other modes. They register in every mode, including with no token at all:
Tool | What it does |
| Turns ids into the URL that addresses them, plus the |
| Navigates the desktop app to a location over its debug port. |
|
|
monday_ui_action drops post_update in read-only mode, so the tool is genuinely
read-only rather than accepting a write and refusing it later. Board grid cells are
not reachable by any of these — the grid is a canvas, not DOM. Switching a board view
and running the Excel export are not implemented.
Column values
Pass values keyed by column id or column title. Use the plain form below.
An object value passes through untouched, so you keep control when you need
the exact API shape.
Column type | Send this | Server sends this |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| the same object |
Send null to clear a column.
Three notes on the awkward ones:
phoneneeds a country as well as a number. An international number carries one, so"+442071234567"works. A local number does not, so send["07700900123", "GB"].locationstores coordinates. monday.com does not turn an address into coordinates, and neither does this server, so sendlatandlng.statusanddropdownreject a label the board does not have. Passcreate_labels_if_missing: trueto add it instead.
These types are not writable, because monday.com computes them:
auto_number, button, creation_log, formula, integration, item_id,
last_updated, mirror, progress, subtasks, time_tracking, vote.
A file or doc column needs the separate upload endpoint, which this
server does not expose.
Safety
Setting | Effect |
| Only read tools get registered. A raw mutation is refused. |
| Every other board becomes invisible. |
| Needs |
The token never appears in a tool result or an error message. The client redacts it before anything leaves the process.
Options
Flag | Environment variable | Default |
|
| none, and the server refuses to start |
|
| none |
|
| off |
|
| all boards |
|
|
|
|
|
|
|
| off |
|
| read from the token |
|
|
|
|
| |
|
|
A flag always wins over the matching environment variable.
Running without a token. --desktop-only starts the server with no token: the
fifteen API tools are absent and only the three addressing tools load, riding the
session already signed in to the desktop app. It must be asked for explicitly — a
missing token is otherwise still a hard failure, so a typo in MONDAY_API_TOKEN fails
loudly instead of quietly booting a server that cannot see your data.
claude mcp add monday-desktop -- node /full/path/to/monday-mcp/dist/index.js \
--desktop-only --account-slug <your-slug>--account-slug matters here: every monday.com URL needs the account subdomain, and
without a token there is nothing to read it from. monday_resolve_location fails with
a clear message rather than emitting a slug-less URL, which would 404.
About the API version. monday.com retires an API version every quarter, and a request that names a retired version quietly gets the maintenance version instead. That makes a stale default worse than no default, so this server pins a current one and you can override it. Check the versioning page when you upgrade.
Rate limits
monday.com meters a complexity budget, not a request count. The client reads
the reset hint from the throttle response, whether it arrives in the
retry-after header or in the message body, and waits for that long. Other
transient failures use exponential backoff.
Retrying stops after about 45 seconds in total. A complexity window can be a full minute, and waiting three of them outlasts every MCP client, so the server reports the throttle and lets the caller decide to try again.
Board layouts stay in a cache for one minute, which keeps a run of writes off the budget.
Mode 1 — the desktop app over CDP (no API token)
The monday.com macOS desktop app is an Electron shell that hosts the web app in a
<webview>. It registers no custom URL scheme (there is no monday://), but its
Electron fuses leave remote debugging open — so you can attach a CDP client and drive the
real, already‑logged‑in web app with no API token. This was verified end‑to‑end:
launching with a debug port exposes a webview target already signed in, and a
zero‑dependency Node client can navigate it and read the item‑card DOM.
scripts/cdp-desktop.mjs is that client (uses Node's native WebSocket; Node ≥ 22, no
npm install). Claude‑in‑Chrome cannot attach here — it pairs with the Chrome
extension only — which is why a small CDP client is used instead.
# 1. Quit any running instance (single-instance lock ignores the flag otherwise)
osascript -e 'quit app "monday.com"'
# 2. Launch with the debug port (launch the binary directly; `open -a --args` is unreliable)
/Applications/monday.com.app/Contents/MacOS/monday.com --remote-debugging-port=9222 &
# 3. Drive it — no token, rides your session
node scripts/cdp-desktop.mjs targets # list debug targets
node scripts/cdp-desktop.mjs open <slug> <board_id> <item_id> # jump straight to an item card
node scripts/cdp-desktop.mjs nav '<url>' # any deep link
node scripts/cdp-desktop.mjs eval '<js>' # read/drive the DOM
node scripts/cdp-desktop.mjs shot out.png # screenshot the webview⚠️ Security — read before using.
--remote-debugging-portopens an unauthenticated port onlocalhost. While it is open, any local process can read and drive your logged‑in monday.com session — no password required. "Any local process" is broad: a browser extension with a native messaging host, background dev tooling, or a maliciousnpmpostinstall script all qualify, and none of them need your credentials to act as you in monday.com. Only run it when you want this, and quit the app when done (osascript -e 'quit app "monday.com"') to close the port. Launching also restarts the app, losing any unsaved in‑app state.
Notes learned while building this: a cold Page.navigate to /boards/<id>/pulses/<item_id>
does open the item card (deep links only appear to "hang" under drivers that wait for
document‑idle; poll for an element instead); Page.navigate destroys the JS execution
context, so re‑evaluate after navigating. The board grid is a <canvas> (see Mode 2), so
individual cells aren't DOM‑selectable — reach a cell through the item card or the API.
Four more, all found by driving a real board and all now handled in src/desktop.ts:
Widen the viewport before you touch the card. The item card is a right‑anchored panel that extends past the window: its submit control measured x=1813 in a 1360px viewport, so a pointer event at its coordinates hits nothing and
elementFromPointreturnsnull.Emulation.setDeviceMetricsOverrideto ~2400×1400 before navigating pulls it into reach. Clear the override afterwards.The update composer is NOT inside
[role="dialog"]— it renders in its own micro‑frontend subtree (new-post-update-MfExternalComponent). Scoping selectors to the dialog finds nothing.document.execCommand("insertText")does not drive the editor's model. Use CDPInput.insertTextso the rich‑text editor actually registers the content; otherwise the text appears in the DOM and the Update control stays inert.The submit control is a
DIV,[data-testid="post-editor-update-button"], so aquerySelectorAll("button")sweep never sees it.
And two traps when verifying a post: take the baseline post count only after the thread settles (counting straight after navigation reads 0, and the existing posts arriving late then look like success), and confirm by content, not count. Unsent text also accumulates in the composer and survives an app relaunch — clear it first.
Mode 2 — the web app in a browser
For UI‑only actions, screenshots, or working with no token. Two sub‑modes share the URL builder above.
Local Chrome (rides your existing login). Drive the DOM with a browser‑automation
tool. Two gotchas, both verified live and documented in
docs/interface-map.md:
The board grid is a
<canvas>. Item names, cell values, and status pills are painted, not DOM — you cannot select a cell by selector on the current renderer. Reach a cell by opening the item card (real DOM), by coordinate click, or via the API. There is also a legacy DOM‑grid renderer still in the wild; detect which is active by checking whether a known item name appears indocument.body.innerText.Cold deep‑link navigation can stall under idle‑waiting drivers; navigate to the account root and click through in‑app, or use raw CDP and poll for a target element.
App chrome, dialogs, menus, and the item card use monday's open‑source Vibe design
system, which puts a stable data-vibe="…" (component type) and data-testid on every
component root. Prefer [data-vibe="Modal"], role="dialog", [data-testid="…"], and
data-group-id over hashed CSS‑module class names. Selector conventions are catalogued in
the interface map.
Remote — Cloudflare Browser Run. For headless/server contexts, connect
playwright-core / puppeteer-core over CDP to Browser Run (Chromium), persist login with
Playwright storageState, and do first login via the Human‑in‑the‑Loop Live View handoff.
Cloudflare KiteSurf is CDP‑reachable but, per Cloudflare's own docs, cannot hold an authenticated session — so it is unsuitable for a logged‑in monday.com. Use Browser Run's default Chromium instead. All Cloudflare browsers egress from datacenter IPs flagged as bot traffic, so logins may be challenged.
Security & privacy
Your token stays local. The API server talks straight to
api.monday.comover HTTPS. No relay, no telemetry. The token is redacted before anything leaves the process, and never appears in a tool result or error.Never commit a token.
.env,*.token, and friends are gitignored; only.env.example(a placeholder) is tracked.Read‑only and board‑allowlist modes cap what the API server can touch (see Safety).
The desktop CDP debug port is unauthenticated — see the boxed warning in Mode 1. Treat it as a temporary, cons⁠ent‑gated handle and close it when done.
Same‑origin authenticated
fetchfrom a browser mode is intentionally out of scope — use the API for data.
How this was mapped
The three‑mode design and every URL/DOM/API detail come from first‑hand investigation, not
guesswork, and each claim in docs/interface-map.md is tagged with
how it was verified: live in a logged‑in browser, by decompiling the desktop app bundle, from
official monday.com / Cloudflare docs, or from maintained third‑party integrations. The
document also lists, verbatim, everything that could not be resolved, so the boundaries of
what's known are explicit. Start there if you want to extend any mode.
Develop
npm test # tests, no network
npm run typecheck # source and tests
npm run build
npm run dev # rebuild on saveThe tests run the real MCP server against a fake monday.com API over an
in-memory transport. They assert the exact JSON that goes over the wire.
test/regressions.test.ts holds one test per defect found so far, named
after the behaviour that was wrong.
Two CDP clients exist, on purpose. scripts/cdp-desktop.mjs is the standalone
CLI documented in Mode 1.
src/desktop.ts is its in-package twin, used by monday_open and
monday_ui_action. The logic is duplicated because scripts/ is not in
package.json files, so a published install cannot reach it. Change one and you
almost certainly need to change the other.
Contributing
See CONTRIBUTING.md. To report a security problem, see SECURITY.md.
Licence
MIT. See LICENSE.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA Model Context Protocol server that provides tools for interacting with Trello boards, enabling seamless management of cards, lists, and activities while handling rate limiting and type safety.34MIT
- AlicenseAqualityDmaintenanceA Model Context Protocol server that enables creating, modifying, and validating infinite canvas data structures according to the JSON Canvas 1.0 specification.714MIT
- AlicenseCqualityDmaintenanceA Model Context Protocol (MCP) server for programmatically creating and managing n8n workflows.118MIT
- FlicenseNot gradedqualityDmaintenanceA Model Context Protocol server that provides tools for connecting to and interacting with various database systems (SQLite, PostgreSQL, MySQL/MariaDB, SQL Server) through a unified interface.3
Related MCP Connectors
Monday.com MCP — wraps the Monday.com GraphQL API (BYO API key)
A Model Context Protocol server for Wix AI tools
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
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/ashrocket/monday-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server