Mural MCP
Allows browsing workspaces and rooms, creating/updating/deleting murals, and managing mural widgets (sticky notes, shapes, text, arrows, areas, comments) via the Mural Public API.
Click on "Deploy 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., "@Mural MCP list my workspaces"
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.
Mural MCP
MCP server for working with Mural Public API v1 from MCP-compatible clients (Cursor, Claude Desktop, and other MCP hosts).
It supports browsing workspaces and rooms, creating/updating/deleting murals, and creating/updating/deleting mural widgets.
Description
This project exposes Mural operations as MCP tools by wrapping Mural's REST API with a Python server built on FastMCP.
Highlights:
OAuth2 authentication using
client_id,client_secret, and a long-livedrefresh_tokenAutomatic access token refresh before expiration
Tooling for:
workspace/room/mural navigation
mural CRUD
widget CRUD (sticky notes, shapes, text, arrows, areas, comments)
Related MCP server: miro-mcp-server
Requirements
Python
>=3.11uv (recommended for dependency management and running)
A Mural developer app with:
MURAL_CLIENT_IDMURAL_CLIENT_SECRETOAuth redirect URI configured in Mural
Installation
Clone the repository and install dependencies:
git clone <your-repo-url>
cd arcsen-mural-mcp
uv syncConfiguration
Create a .env file in the project root:
cp .env.example .envFill in:
MURAL_CLIENT_ID=your-client-id
MURAL_CLIENT_SECRET=your-client-secret
MURAL_REFRESH_TOKEN=your-refresh-token
# Optional but recommended:
MURAL_REDIRECT_URI=http://localhost:8080/callbackGet Initial Refresh Token (One-Time OAuth Setup)
If you only have client ID and secret, generate the first refresh token with:
./scripts/get_mural_refresh_token.shWhat it does:
Reads
MURAL_CLIENT_IDandMURAL_CLIENT_SECRETfrom.envBuilds OAuth authorization URL
Prompts you for the returned authorization
codeExchanges code for tokens and prints
MURAL_REFRESH_TOKEN=...
Then paste that value into your .env.
Note: redirect URI must match exactly between:
your Mural app settings
the authorization request
the token exchange request
Run the MCP Server
Start server over stdio:
uv run main.pyThis process is intended to be launched by an MCP host (rather than visited in a browser).
Connect to Claude Desktop
Edit:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
Example:
{
"mcpServers": {
"mural": {
"command": "uv",
"args": [
"run",
"--directory",
"/absolute/path/to/arcsen-mural-mcp",
"main.py"
],
"env": {
"MURAL_CLIENT_ID": "your-client-id",
"MURAL_CLIENT_SECRET": "your-client-secret",
"MURAL_REFRESH_TOKEN": "your-refresh-token"
}
}
}
}Restart Claude Desktop after saving.
Available Tool Groups
The server includes the following tool categories:
Navigation / Read
list_workspaceslist_roomslist_murals_in_roomget_muralget_mural_widgetsget_mural_widget
Mural CRUD
create_muralupdate_muraldelete_mural
Widget Create
create_sticky_notescreate_shapescreate_titlescreate_textboxescreate_arrowcreate_areacreate_comment
Widget Update
update_sticky_noteupdate_shapeupdate_textupdate_arrowupdate_areaupdate_comment
Widget Delete
delete_widget
Arrow Accuracy Tip
To avoid randomly placed arrows, prefer anchored connectors:
Use
create_arrowwithstart_ref_idandend_ref_idUse widget IDs from created/fetched source and target shapes
Avoid relying only on raw
x/yandpoints
Troubleshooting
RuntimeErrorfor missing env vars
EnsureMURAL_CLIENT_ID,MURAL_CLIENT_SECRET, andMURAL_REFRESH_TOKENare present.OAuth redirect shows "site can't be reached"
This can still be fine if URL contains?code=.... Copy the code from the URL and continue.OAuth redirect returns 404 on local server
Usually means redirect reached your local server but no matching path exists. You can still copycodefrom the URL.No tools appear in Claude Desktop
Validate JSON config, confirm absolute path, and restart Claude Desktop fully.
Development Notes
Entry point:
main.pyAuth and token refresh logic:
MuralClientclass inmain.pyOAuth helper script:
scripts/get_mural_refresh_token.sh
License
Add your project license here (for example, MIT).
Available Tools
23 toolscreate_areaA
Create an area (swimlane/zone) widget on a mural.
Args:
mural_id: The mural ID.
x: Horizontal position in px (default 0).
y: Vertical position in px (default 0).
width: Width in px (default 460).
height: Height in px (default 503).
title: Title text displayed on the area.
layout: "free" (default), "column", or "row".
show_title: Whether to display the title (default true).
style: { backgroundColor, borderColor, borderStyle ("solid"|"dotted"), borderWidth (1-7), titleFontSize }.
parent_id: ID of parent area widget (for nested areas).
Inputs: mural_id (required), x, y, style, +6 more. Returns: result
create_arrowA
Create an arrow (connector) widget on a mural.
Args:
mural_id: The mural ID.
x: Horizontal position in px.
y: Vertical position in px.
width: Width of the bounding box in px.
height: Height of the bounding box in px.
points: Array of {x, y} coordinate objects defining the arrow path (min 2 points).
arrow_type: "straight" (default), "curved", or "orthogonal".
tip: "single" (default), "double", or "no tip".
start_ref_id: ID of the widget the arrow starts from (snaps to it).
end_ref_id: ID of the widget the arrow ends at (snaps to it).
style: { strokeColor (hex+alpha), strokeStyle ("solid"|"dashed"|"dotted-spaced"|"dotted"), strokeWidth (1-7) }.
label: { format: {color, fontFamily, bold, italic, textAlign, fontSize}, labels: [{x, y, height, width, text}] }.
parent_id: ID of parent area widget.
Inputs: x (required), y (required), width (required), height (required), +9 more. Returns: result
create_commentA
Create a comment widget on a mural.
Args:
mural_id: The mural ID.
x: Horizontal position in px.
y: Vertical position in px.
message: The comment text.
reference_widget_id: ID of the widget this comment is attached to.
parent_id: ID of parent area widget.
Inputs: x (required), y (required), message (required), mural_id (required), +2 more. Returns: result
create_muralA
Create a new mural in a room.
Args:
room_id: The room ID to create the mural in (required).
title: Title of the mural.
background_color: Background color in hex with alpha (e.g. "#FFFFFFFF").
width: Width of the mural in px (3000-60000, default 9216).
height: Height of the mural in px (3000-60000, default 6237).
infinite: If true, canvas is borderless and grows as you add widgets.
Inputs: room_id (required), title, width, height, +2 more. Returns: result
create_shapesA
Create one or more shape widgets on a mural (limit 1000).
Each widget object supports:
- x, y (required): Position in px.
- shape (required): One of: ellipse, rectangle, rounded_square, rhombus_smart, triangle_smart,
hexagon_smart, pentagon_smart, diamond, process, decision, terminator, start, end, delay,
data, database, document, multiple_documents, predefined_process, manual_input, manual_loop,
preparation, stored_data, connector, merge, or, summing_junction, display, direct_data,
internal_storage, papertape, loop_limit, off_page_connector, cloud, cross, star, badge,
ribbon, step, trapezoid, octagon, speech_bubble_left, speech_bubble_right, speech_bubble_center,
thinking_bubble_left, thinking_bubble_right, arrow_right, arrow_left, arrow_down, arrow_top,
arrow_left_right, brace_left, brace_right, note_left, note_right, teardrop_bubble, right_triangle.
- text: Plain text content.
- htmlText: HTML-formatted text.
- width, height: Size in px (default 138).
- style: { backgroundColor, borderColor, borderStyle, borderWidth, bold, italic, underline, strike, font, fontColor, fontSize, textAlign }.
- parentId: ID of parent area widget.
Args:
mural_id: The mural ID.
widgets: Array of shape objects.
Inputs: widgets (required), mural_id (required). Returns: result
create_sticky_notesA
Create one or more sticky note widgets on a mural (limit 1000).
Each widget object supports:
- x, y (required): Position in px.
- shape (required): "circle" or "rectangle".
- text: Plain text content.
- htmlText: HTML-formatted text (overrides text).
- width, height: Size in px (default 138).
- style: { backgroundColor, bold, italic, underline, strike, font, fontSize, textAlign, border }.
- parentId: ID of parent area widget.
- tags: Array of tag IDs.
Args:
mural_id: The mural ID.
widgets: Array of sticky note objects.
Inputs: widgets (required), mural_id (required). Returns: result
create_textboxesA
Create one or more textbox widgets on a mural (limit 1000). Textboxes wrap text to fixed width.
Each widget object supports:
- text: The textbox text (supports HTML: <b>, <i>, <u>, <s>, <span style="color:...">>).
- x, y: Position in px (default 0).
- width: Width in px (default 296).
- height: Height in px (default 28).
- style: { backgroundColor, font, fontSize (default 24), textAlign }.
- parentId: ID of parent area widget.
Args:
mural_id: The mural ID.
widgets: Array of textbox objects.
Inputs: widgets (required), mural_id (required). Returns: result
create_titlesA
Create one or more title widgets on a mural (limit 1000). Titles auto-grow width to fit text.
Each widget object supports:
- text: The title text (supports HTML: <b>, <i>, <u>, <s>, <span style="color:...">>).
- x, y: Position in px (default 0).
- width: Width in px (default 293).
- height: Height in px (default 62).
- style: { backgroundColor, font, fontSize (default 48), textAlign }.
- parentId: ID of parent area widget.
Args:
mural_id: The mural ID.
widgets: Array of title objects.
Inputs: widgets (required), mural_id (required). Returns: result
delete_muralB
Delete a mural.
Args:
mural_id: The mural ID to delete.
Inputs: mural_id (required). Returns: result
delete_widgetB
Delete any widget from a mural by its ID.
Args:
mural_id: The mural ID.
widget_id: The widget ID to delete.
Inputs: mural_id (required), widget_id (required). Returns: result
get_muralA
Get details of a single mural.
Args:
mural_id: The mural ID (format: "workspaceId.timestamp", e.g. "workspace1234.1608152669000").
Inputs: mural_id (required). Returns: result
get_mural_widgetC
Get a single widget by its ID.
Args:
mural_id: The mural ID.
widget_id: The widget ID.
Inputs: mural_id (required), widget_id (required). Returns: result
get_mural_widgetsA
Get all widgets on a mural. Use type_filter to narrow results.
Args:
mural_id: The mural ID.
type_filter: Comma-separated widget types to filter. Options: areas, arrows, comments, files, sticky notes, texts, icons, images, shapes.
parent_id: Filter widgets by their parent area ID.
limit: Max results to return.
next_token: Pagination token from a previous response.
Inputs: mural_id (required), limit, parent_id, next_token, +1 more. Returns: result
list_murals_in_roomA
List murals in a room.
Args:
room_id: The room ID (integer).
limit: Max results to return.
next_token: Pagination token from a previous response.
Inputs: room_id (required), limit, next_token. Returns: result
list_roomsA
List rooms in a workspace that the authenticated user has access to.
Args:
workspace_id: The workspace ID.
limit: Max results to return.
next_token: Pagination token from a previous response.
Inputs: workspace_id (required), limit, next_token. Returns: result
list_workspacesA
List all workspaces the authenticated user is a member of.
Args:
limit: Max results to return.
next_token: Pagination token from a previous response.
Inputs: limit, next_token. Returns: result
update_areaA
Update an area (swimlane/zone) widget on a mural.
Args:
mural_id: The mural ID.
widget_id: The widget ID of the area.
properties: Object with fields to update. Supported fields:
x, y, width, height, title, layout ("free"|"column"|"row"), showTitle,
style: { backgroundColor, borderColor, borderStyle, borderWidth, titleFontSize },
parentId, hidden.
Inputs: mural_id (required), widget_id (required), properties (required). Returns: result
update_arrowA
Update an arrow (connector) widget on a mural.
Args:
mural_id: The mural ID.
widget_id: The widget ID of the arrow.
properties: Object with fields to update. Supported fields:
x, y, width, height, points, arrowType ("straight"|"curved"|"orthogonal"),
tip ("single"|"double"|"no tip"), startRefId, endRefId,
style: { strokeColor, strokeStyle, strokeWidth },
label: { format: {...}, labels: [...] }, parentId.
Inputs: mural_id (required), widget_id (required), properties (required). Returns: result
update_commentA
Update a comment widget on a mural.
Args:
mural_id: The mural ID.
widget_id: The widget ID of the comment.
properties: Object with fields to update. Supported fields:
x, y, message, referenceWidgetId, parentId.
Inputs: mural_id (required), widget_id (required), properties (required). Returns: result
update_muralC
Update a mural's properties.
Args:
mural_id: The mural ID.
title: New title.
background_color: New background color in hex with alpha.
Inputs: mural_id (required), title, background_color. Returns: result
update_shapeA
Update a shape widget on a mural.
Args:
mural_id: The mural ID.
widget_id: The widget ID of the shape.
properties: Object with fields to update. Supported fields:
x, y, width, height, rotation, text, htmlText,
style: { backgroundColor, borderColor, borderStyle, borderWidth, bold, italic, underline, strike, font, fontColor, fontSize, textAlign },
title, parentId, hidden.
Inputs: mural_id (required), widget_id (required), properties (required). Returns: result
update_sticky_noteB
Update a sticky note widget on a mural.
Args:
mural_id: The mural ID.
widget_id: The widget ID of the sticky note.
properties: Object with fields to update. Supported fields:
x, y, width, height, rotation, text, htmlText,
style: { backgroundColor, bold, italic, underline, strike, font, fontSize, textAlign, border },
tags, title, hyperlink, hyperlinkTitle, parentId, hidden.
Inputs: mural_id (required), widget_id (required), properties (required). Returns: result
update_textA
Update a text widget (title or textbox) on a mural.
Args:
mural_id: The mural ID.
widget_id: The widget ID of the text/title/textbox.
properties: Object with fields to update. Supported fields:
x, y, width, height, rotation, text,
style: { backgroundColor, font, fontSize, textAlign },
title, hyperlink, hyperlinkTitle, parentId, hidden.
Inputs: mural_id (required), widget_id (required), properties (required). Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 23 tools
Each tool targets a distinct resource and action: workspace, room, mural, and specific widget types (sticky note, shape, title, textbox, arrow, area, comment). The list/get/create/update/delete operations are clearly separated, and even similarly named tools like get_mural_widgets vs get_mural_widget are unambiguous.
The overall pattern is consistent verb_noun (list_* get_* create_* update_* delete_*). Minor deviations include plural create_sticky_notes/create_shapes vs singular create_arrow/create_area, and generic delete_widget vs delete_mural, but these are readable and predictable.
With 23 tools, the set is on the heavy side but appropriate for the domain, covering mural CRUD plus multiple widget types. It feels slightly bloated because widget creation/update could potentially be consolidated, but each tool serves a distinct purpose.
The core lifecycle for murals and widgets is well covered: list/get/create/update/delete for murals, and get/create/update/delete for widgets. Missing workspace/room creation and a few niche widget operations are minor gaps that agents can work around.
Maintenance
Related MCP Connectors
The official MCP Server for the Mux API
MCP server for interacting with the Supabase platform
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
Query, browse, and automate OmegaAI workspaces from any MCP client. Streamable HTTP with OAuth 2.0.
Related MCP Servers
- AlicenseBqualityDmaintenanceMiro MCP server, exposing all functionalities available in official Miro SDK.97289 npm65Apache 2.0
- AlicenseAqualityBmaintenanceMCP server for controlling Miro whiteboards with AI assistants. Create and manage stickies, shapes, frames, connectors, and more.8831MIT
- AlicenseNot gradedqualityDmaintenanceThis repository contains an implementation of an Miro Universal MCP (Model Context Protocol) server. It provides a standardized interface for interacting with Miro's tools and services through a unified API.1MIT
- AlicenseBqualityCmaintenanceMCP server for the Mural visual collaboration platform enabling board content editing with sticky notes, shapes, text boxes, areas, images, connectors, plus navigation and mural management.2028 npm2MIT