customaise
OfficialClick 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., "@customaiselist my user scripts"
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.
@customaise/mcp
MCP server and CLI that connect AI coding agents to the Customaise Chrome extension. Manage UserScripts, build AgentScripts, call WebMCP tools inside the user's signed-in browser session, select DOM elements visually, and drive tabs. Drive it over stdio from an IDE, or as a customaise command from a shell.
19 tools, 5 resources, WebSocket bridge between your agent and a real Chrome session, whichever door it arrives through.
Two ways in. customaise-mcp is the MCP server an IDE spawns over stdio.
customaise is a CLI for agents that have a shell instead, driving the same
tools through a resident daemon. Both go through the same cap enforcement and
the same consent gate.
AI Agent ←(stdio)→ MCP Server ←(WebSocket)→ Customaise ExtensionSpeaks the MCP 2026-07-28 revision, negotiated per connection: a client on
that revision gets the stateless flow with cacheable tools/list, and a client
on the 2025 revisions keeps working unchanged. The package is 3.x precisely so
nobody has to guess this from a 2.x version number.
Quick Start
1. Install Customaise
Install the Customaise Chrome extension and enable MCP Bridge in Settings.
2. Add to your IDE
Cursor (.cursor/mcp.json):
{
"mcpServers": {
"customaise": {
"command": "npx",
"args": ["-y", "@customaise/mcp"]
}
}
}Claude Code (one command, no file to edit):
claude mcp add customaise -- npx -y @customaise/mcpClaude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"customaise": {
"command": "npx",
"args": ["-y", "@customaise/mcp"]
}
}
}Windsurf (.windsurf/mcp.json):
{
"mcpServers": {
"customaise": {
"command": "npx",
"args": ["-y", "@customaise/mcp"]
}
}
}Kiro (.kiro/mcp.json):
{
"mcpServers": {
"customaise": {
"command": "npx",
"args": ["-y", "@customaise/mcp"]
}
}
}Codex (~/.codex/config.toml):
[mcp_servers.customaise]
command = "npx"
args = ["-y", "@customaise/mcp"]Antigravity (mcp_config.json):
{
"mcpServers": {
"customaise": {
"command": "npx",
"args": ["-y", "@customaise/mcp"]
}
}
}3. Done
Your agent can now read and edit UserScripts, build AgentScripts that expose WebMCP tools to it, select DOM elements visually, inspect the console, and take screenshots of the live tab.
Related MCP server: Chromanche
Tools (19)
Script Lifecycle
Tool | Description |
| List every script (UserScripts and AgentScripts) managed by the extension |
| Pull a script to a local file for editing |
| Push a local file to Customaise (validates and installs) |
| Permanently delete a script |
| Enable or disable a script |
Browser Context
Tool | Description |
| DOM snapshot of the current page |
| Console logs, errors, and |
| List all open browser tabs |
Tab Control
Tool | Description |
| Open a new tab at a given URL |
| Close a tab by ID |
| Switch focus to a tab by ID |
| Reload a tab to re-inject scripts |
Visual DOM Targeting
Tool | Description |
| Get the DOM elements the user has visually selected, with bulletproof selectors and screenshots |
| Capture any tab (not just the visible one) as a viewport or full-page image |
WebMCP Agent Tools
Tool | Description |
| List the WebMCP tools currently registered on a tab by AgentScripts |
| Call a WebMCP tool; prompt-gated tools block on user consent (see below) |
UI Control & Batch
Tool | Description |
| Show or hide the Customaise UI overlay |
| Bulk export all scripts to a local directory |
Diagnostics
Tool | Description |
| Report extension attachment, plan tier, sign-in, and remaining daily and weekly quota. Costs no quota itself. |
Resources (5)
Five resources any connected agent can read via resources/read. The two conventions handbooks define exactly how Customaise expects UserScripts and AgentScripts to be written. Agents should read the relevant handbook before touching a script.
URI | Description |
| Live JSON list of every script the extension manages (ID, name, enabled state, match patterns, shared flag) |
| Full source and metadata for a specific script |
| Points at the right handbook for the script type you're working on |
| Full UserScript reference: file structure, IIFE pattern, |
| Full AgentScript reference: the |
WebMCP Tool Calls & Consent (HITL)
AgentScripts register tools on web pages via navigator.modelContext.registerTool(...). Each tool is declared in the AgentScript's // @webmcp <toolName> <permission> header with one of three permissions:
allow: tool executes immediately. ~50 to 100ms round-trip per call (the extension still runs permission checks).prompt: every call surfaces an in-browser consent modal and blocks until the user approves or denies. Up to 5 minutes. Design for this. Don't chain prompt-gated calls in tight loops, and treat a longcall_webmcp_toolas normal.deny: tool is suppressed and calls fail immediately.
"Always allow" and "Always deny" buttons on the consent modal persist the decision per-script per-tool until the user resets it in extension Settings. These overrides live in chrome.storage.local on the user's device; the MCP server has no visibility into them.
Remote approvals (optional)
If the user has Power User and has enabled Remote HITL Approvals on their Customaise account page, prompt-gated calls are also mirrored there. They can approve or deny from any signed-in browser, including a phone. Either the extension modal or the remote surface can resolve; first signed decision wins. From the MCP client's perspective this is transparent: call_webmcp_tool simply returns the result when any authorised surface approves, or an error if denied or timed out.
What MCP clients see
A prompt-gated
call_webmcp_toolresponse may take up to 5 minutes. Surface a pending state to the end user rather than timing out aggressively.While it waits, the server sends
notifications/progresson any request that carried aprogressToken, one per extension of the consent budget. A client that resets its request timeout on progress (the MCP SDK does, so does Claude Code) waits with the user. Claude Desktop and Cursor cap a tool call at a fixed 60 seconds at the time of writing and will report the call failed while the user can still approve it; the tool description tells the agent not to blindly re-issue a call with side effects when that happens.If the user denies,
call_webmcp_toolreturns an error. The MCP server does not retry.Tool-call arguments transit HTTPS in plaintext to our backend and land KMS-encrypted at rest in Firestore. Metadata (toolName, scriptName, origin) stays plaintext. See the Customaise Privacy Policy.
Visual DOM Selection
Users can visually select elements in the browser, and the extension pushes context files to your workspace in real time:
.customaise/dom-context/<script-name>/
├── element-name.dom.md # Selectors, element context, user comments
├── element-name.screenshot.png # Cropped screenshot of the selected element
└── ...Where are the files saved?
The MCP server writes .customaise/ to its current working directory (usually your project root in Cursor or Windsurf).
If you are using a global IDE like Claude Desktop, it defaults to your home directory (~/.customaise/). To force a specific project folder, set CUSTOMAISE_WORKSPACE in your MCP config:
"env": { "CUSTOMAISE_WORKSPACE": "/absolute/path/to/your/project" }From the CLI, the directory you ran the command in wins over both. The
daemon is long-lived and was started from whatever directory you happened to
be in the first time, so it takes the caller's word for it on every command.
CUSTOMAISE_WORKSPACE still beats a plain cwd for IDE servers, unchanged.
Use get_selected_elements to retrieve selections programmatically, or read the pushed .dom.md files directly from the workspace.
Each selection includes bulletproof tiered selectors (stable IDs → data attributes → ARIA → semantic classes → structural positioning) so targeting survives page updates.
Workflows
UserScript
1. get_page_context → understand the target page
2. User selects elements → .dom.md files auto-pushed to workspace
3. Write .user.js file → AI writes the script using IDE tools
4. export_script → Customaise validates and installs
5. reload_tab → re-inject the script
6. get_console_context → check for errors
7. take_screenshot → verify the visual resultAgentScript
1. Read customaise://agentscript-conventions → get the structure right before writing
2. get_page_context → find stable selectors on the target page
3. Write .agent.js file → declare tools via // @webmcp, register with navigator.modelContext.registerTool()
4. export_script → Customaise validates and injects
5. reload_tab → the AgentScript registers its tools in the page
6. list_webmcp_tools → confirm tools surfaced
7. call_webmcp_tool → invoke one; prompt-gated calls wait for user consentFile Sync
Use sync_scripts to bulk-export every script to a local directory:
sync_scripts({ directory: "~/customaise-scripts" })This creates:
One
.user.jsfile per script. Filename is derived from the script name (lowercase, hyphens, e.g.my-cool-script.user.js)..customaise-manifest.json: maps filenames to script IDs for round-trip editing.
Manifest format
{
"dark-mode-fix.user.js": "vm_script_1774225715376_lus75sdzn",
"my-cool-script.user.js": "vm_script_1774225800123_abc12defg"
}Round-trip
sync_scriptsexports all scripts to a directory.Edit any
.user.jsfile in your IDE.export_scriptwith the file path andscriptIdfrom the manifest updates that script.Omit
scriptIdwhen callingexport_scriptto create a new script instead.
File watcher (auto-export)
Once sync_scripts has been called, the MCP server watches the directory for .user.js changes. Saving a file in your IDE pushes it to Customaise automatically, no manual export_script needed.
Configuration
Environment Variable | Default | Description |
|
| WebSocket server port |
| (empty) | Comma-separated list of extra extension IDs allowed to connect. Needed for unpacked dev builds with a non-standard extension ID |
| (unset) | Set to |
| (cwd) | Absolute path where |
|
| Where the CLI keeps its daemon connection file and remembered tab. Delete this directory to remove everything the CLI stores; uninstalling the extension does not, because these live outside the browser profile |
|
| Loopback port the daemon serves the CLI on. Distinct from |
|
| Where |
|
| Ceiling on an inline JSON payload. Over it, lists are shortened (never the JSON itself, so it still parses) and the response reports exactly what was dropped |
|
| Ceiling on an inline |
Chat clients and file-less agents
get_page_context, get_console_context and take_screenshot spool their
full payload to a file by default and hand back a summary plus a path. That is the right shape
for an IDE agent: a DOM snapshot is routinely hundreds of KB and belongs on
disk rather than in a context window.
It is a dead end for a chat client. Claude Desktop runs this server over stdio, so the write succeeds, but the model on the other end has no filesystem tool with which to open what was written. It receives a path it can never read.
Three ways out, in the order they are consulted:
Per call. Pass
output: "inline"and the full payload comes back in the response, with nothing written to disk. All three tools say so in their own descriptions and in every file-mode response, so an agent that hits the dead end can recover on its own in one extra call.Per install. Set
CUSTOMAISE_MCP_OUTPUT=inlinein the server's env. The.mcpbbundle ships with this set, because that bundle is installed into Claude Desktop and nowhere else. For an IDE, put it in theenvblock of your MCP config. For the CLI, exporting it in your shell is enough: thecustomaisebinary reads it and forwards it per call, so it does not matter that the resident daemon was started earlier without it.Default.
file, unchanged.
take_screenshot is the same flag with a different payload: output: "inline"
attaches the capture to the response as an MCP image block rather than writing
a PNG and returning its path, so a multimodal chat client can actually see it.
An image cannot be shortened the way a snapshot can, so a capture over
CUSTOMAISE_MCP_INLINE_IMAGE_MAX_KB (1536 KB of base64) falls back to a file
and tells the caller to retry with fullPage: false, which is usually small
enough, or to raise the ceiling.
Inline JSON responses are capped at CUSTOMAISE_MCP_INLINE_MAX_KB (64 KB by
default). Over the cap the payload is shortened by trimming lists, not by
truncating the JSON text, so what arrives still parses and still has every
key and nesting level. The response carries truncated: true and an
omitted array naming each shortened list and how many items it lost.
There is deliberately no client detection here. clientInfo.name is a
guessing game, and the roots capability says the client declares project
directories, not that the model can read them. It is also unavailable on
2026-07-28 connections. An explicit flag with a self-advertising
fallback beats a heuristic that is confidently wrong.
Publishing the public mirror
This package is developed in a private monorepo and mirrored to
getcustomaise/customaise-mcp,
which is what package.json repository points at and what MCP scanners
and directories read. Keep them in step with the sync script rather than by
hand: the previous hand-copy shipped 7 of 21 source files, so the published
repo did not compile and carried none of the test suites.
npm run mirror:plan # what would be published, as JSON
npm run mirror:verify -- ../customaise-mcp # drift report, exit 1 if adrift
npm run mirror:apply -- ../customaise-mcp # copy the plan over a checkoutmirror:apply writes into a clone, deletes files this package no longer
ships, and leaves the repo's own .github/ furniture alone. It never
pushes. Review git diff in the checkout and push yourself.
The plan is built from git ls-files, so anything uncommitted is invisible
to it. The script refuses to run while untracked files exist under mcp/
rather than publishing a tree that is missing them. src/__tests__/public-mirror.test.ts
guards the shape of the plan in CI; the drift check needs a checkout and so
stays a release step.
Security Boundary
The MCP server listens on ws://localhost:4050 in plaintext on your loopback interface. The connection is authenticated by an HTTP Origin header allowlist:
Allowed:
chrome-extension://anmpijcpaobaabcdncjjmnhdeibipmko(production) andchrome-extension://ijjaffggglamocdapoihpkcpealflopp(staging). Chrome stamps this header automatically on WebSocket handshakes from extension service workers; you don't configure anything.Rejected: regular web pages (
https://...), unknown extension IDs, and handshakes with no Origin header. Returns HTTP 403.
What this stops: a malicious webpage opening new WebSocket('ws://localhost:4050') and calling WebMCP tools behind your back. This is the most likely abuse vector.
What this does NOT stop: a malicious native process running as your user. Node's ws client (and most HTTP libraries) lets callers forge any Origin header. If you can't trust processes running as your OS user, the threat model is already broader than this bridge.
Defense in depth: every prompt-permissioned tool still requires your explicit approval in the Customaise consent modal before running.
Tools declared allow run without asking, with one exception that matters here: a script written through this bridge or the customaise CLI does not get to grant itself allow. Its self-declared allow resolves as prompt, so the first call shows you what the agent built. Choosing "Always allow" stores an override and it never prompts again. An agent that could clear its own gate would not be gated, and the whole point of the consent modal is that it lives somewhere the calling agent cannot reach.
Scripts you wrote yourself, and scripts you subscribed to from the marketplace, are unaffected: allow means allow. For marketplace scripts that means the old advice still holds, so only subscribe to AgentScripts from sources you trust.
Dev builds: if you load an unpacked extension with a custom key, set CUSTOMAISE_MCP_EXTRA_EXTENSION_IDS=<your-extension-id> in the MCP server's env.
CLI
For an agent with a terminal rather than an MCP client.
npm i -g @customaise/mcp # both binaries on PATH
customaise doctor # bridge, sign-in, tier, quota, and whether
# "Allow user scripts" is on. Costs no quota.
customaise init # writes AGENTS.md in this project, so the next
# agent finds the CLI without being toldcustomaise scripts list
customaise scripts install ./my-tool.agent.js
customaise scripts get mcp_script_123 -o ./my-tool.agent.js
customaise scripts enable mcp_script_123 # or disable
customaise scripts fork shared_abc -o ./mine.agent.js
customaise scripts rm mcp_script_123
customaise sync ./customaise-scripts # bulk export your scripts
customaise tabs
customaise tab reload 42
customaise use --tab 42 # remember it for later commands
customaise tools # WebMCP tools on that tab
customaise call my_tool --args '{"q":"hi"}'
customaise context page # DOM snapshot
customaise shot -o ./page.png
customaise tab list # every short verb has a noun-verb form: tab list|shot|use
customaise schema # the whole command tree as JSON, for an agent to read
customaise daemon status | stopWithout a global install, npx -p @customaise/mcp customaise <verb> works but
costs roughly half a second of package resolution per command against about
fifty milliseconds installed. For anything in a loop, install it.
Output contract. JSON on stdout, always, so it pipes. Diagnostics on
stderr. --pretty indents the JSON; -h or --help prints usage, and customaise schema prints the whole command tree as JSON for an agent to read. Exit codes are the interface:
Code | Meaning |
0 | success |
2 | usage error |
3 | daemon or extension unreachable, including after signing out or with "Allow user scripts" off |
4 | signed in, but the token expired or could not be refreshed |
5 | free-tier cap reached |
6 | consent denied by the user |
7 | consent expired unanswered |
8 | rejected by Customaise (diagnostics in the payload) |
Two things make 3 more common than it looks. Signing out of Customaise tears
the bridge down deliberately, so it reports 3, not 4. And the "Allow user
scripts" toggle on the Customaise card in chrome://extensions resets on every
Chrome restart; while it is off, scripts install fine and no tool ever
registers, so doctor reports 3 rather than claiming a healthy setup that
cannot run anything. 4 is the narrower case: the bridge is up and the token behind
it went stale. Both mean stop, and both are worth telling the user about, but
only 3 is worth checking Chrome over.
Codes 5, 6 and 7 are deliberately distinct: an agent that cannot tell a cap from a refusal from a timeout retries into a wall. Code 8 is the one to handle first when installing scripts: the sanitization pipeline refused the file and the diagnostics say what to change, where a 1 means something broke and rewriting the script will not help.
The daemon. Started on first use, and it holds the WebSocket to the
extension so commands do not each pay for a reconnect. It binds 127.0.0.1
only and authenticates the CLI with a token in a 0600 file that exists only
while the endpoint is live. customaise daemon stop ends it.
When several customaise-mcp processes share a machine, the first to bind
:4050 leads and the rest relay through it, so one extension serves every
IDE and every shell at once. That seam carries its own protocol version: a
process built against different frames is refused rather than served, with an
error naming both versions and which one to restart (-40033). Package
versions may differ freely; only a change to the frames themselves moves it.
A leader that sees a follower from a newer package steps down and rejoins
behind it, so a resident daemon can never pin the machine to an old version.
Requirements
Node.js ≥ 20
Chrome with the Customaise extension installed (≥ 1.2.3 for the v2 bridge protocol; older extensions still work but don't surface the cap-usage display)
MCP Bridge enabled in Customaise Settings (free, signed-in)
Plan tiers
The MCP Bridge is free for any signed-in Customaise user. Free use is capped at 50 calls per UTC day and 150 calls per rolling 7-day window. Power User unlocks unlimited MCP. The cap covers every successful tool dispatch (built-in tools and WebMCP calls alike); failed calls and protocol-level traffic don't count.
When the cap fires, the server returns a JSON-RPC error with code -40029 and a human-readable message + structured data carrying scope, used/limit, and reset timestamp. IDEs that surface tool errors render the message verbatim. Sign-in is required regardless of tier; without a fresh Firebase ID token the server returns -40028 MCP_AUTH_REQUIRED. Branch on structuredContent.error.type, never on the number: the codes moved once already (3.2.0 took them out of the range the 2026-07-28 specification reserved for itself; the old -3202x values are still accepted from older extensions), the type strings did not.
Troubleshooting
"Customaise extension is not connected"
Make sure Chrome is running with the Customaise extension.
Check that MCP Bridge is enabled in extension Settings.
The extension connects automatically within a few seconds.
Port conflict on 4050
Set a different port:
CUSTOMAISE_WS_PORT=4051 npx @customaise/mcp.
Scripts not running after export
Call
reload_tabto trigger script re-injection.Check the
@matchpattern covers the current URL.
call_webmcp_tool hangs for minutes
The tool is
prompt-gated. The user has to approve in the browser, or remotely if Remote HITL Approvals is on. 5-minute budget before auto-deny. Surface a pending state rather than timing out.
call_webmcp_tool returned an error like "consent denied"
Expected when the user denied the modal, the 5-minute budget expired, or a previous "Always deny" override was set on that tool. The user can reset per-tool overrides in extension Settings.
list_webmcp_tools returns empty after a reload
Walk the conventions handbook's troubleshooting checklist. Most common: the global AgentScripts toggle in Customaise Settings is off, or the
@matchpattern doesn't cover the URL. Seecustomaise://agentscript-conventionsfor the full list.
License
MIT
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 gradedqualityBmaintenanceEnables AI agents to programmatically control and automate VSCode by interacting with its UI, executing commands, and inspecting the DOM structure. It supports advanced workflows like UI testing, extension development, and debugging through a standalone VSCode instance.108MIT
- FlicenseNot gradedqualityDmaintenanceLets any MCP-capable coding agent drive your real, logged-in Chrome through a local MCP server and MV3 extension.5
- AlicenseNot gradedqualityBmaintenanceLets AI coding agents control and inspect a live Chrome browser via MCP, providing Chrome DevTools capabilities for automation, debugging, and performance analysis.11Apache 2.0
- AlicenseNot gradedqualityDmaintenanceBridges a browser extension with an AI agent via MCP, enabling the agent to capture and act on UI elements, styles, and accessibility data from web pages.15ISC
Related MCP Connectors
Live browser debugging for AI assistants — DOM, console, network via MCP.
A paid remote MCP for AI agent browser DevTools MCP, built to return verdicts, receipts, usage logs,
StremAI MCP: shared memory for AI coding agents. Connected agents can recall. OAuth + local stdio.
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/getcustomaise/customaise-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server