Let AI agents (Claude, etc.) manage your Violentmonkey userscripts through the Model Context Protocol.
Violentmonkey runs inside the browser and cannot listen on a port, so a small local server bridges the two sides:
┌────────┐ MCP (stdio or HTTP) ┌──────────────────┐ WebSocket ┌───────────────┐
│ Agent │ ────────────────────► │ @violentmonkey/ │ ◄──────────── │ Violentmonkey │
│(Claude)│ ◄──────────────────── │ mcp (server) │ ──────────► │ (extension) │
└────────┘ └──────────────────┘ tool calls └───────────────┘
- The agent speaks MCP to the server.
- The extension connects to the server over a WebSocket, authenticated with a one-time token.
- The server owns the list of tools and their schemas. Agent tool calls are forwarded to the extension and the results are returned.
- Violentmonkey >= 2.49.5, which added MCP support. Per-tool minimums are listed under Tools.
- Node.js >= 24
Claude Code:
claude mcp add violentmonkey -- npx -y @violentmonkey/mcp -p 5678Claude Desktop / any stdio-based client (mcpServers config):
{
"mcpServers": {
"violentmonkey": {
"command": "npx",
"args": ["-y", "@violentmonkey/mcp", "-p", "5678"]
}
}
}Ask the agent to call vm_status. It returns a connect URL such as:
https://violentmonkey.github.io/mcp_connect.html?port=5678&token=<generated_token>
Open it in the browser. Violentmonkey intercepts the URL and shows an authorization page; after you approve, the extension connects to the server and the tools become usable.
The same URL is also printed to stderr when the server starts.
"List my userscripts", "Create a script that hides the sidebar on example.com", "Fix the bug in my GitHub script".
npx @violentmonkey/mcp [options]
-p, --port <number> Port for the extension (and HTTP transport). Default: 5678
--host <host> Bind address. Default: 127.0.0.1
--token <string> Use a fixed token instead of generating one. Env: VM_MCP_TOKEN
--readonly Only expose read-only tools (no write, enable or delete)
--transport <type> MCP transport: "stdio" (default) or "http"
stdio (default) - the agent spawns the server as a child process. Nothing is written to stdout except MCP messages; logs go to stderr. The server lives as long as the agent session.
http - the server runs standalone and serves Streamable HTTP MCP at POST /mcp on the same port as the extension WebSocket. Use this when the agent is not on the same machine's process tree (remote agents, other devices). Requests must carry Authorization: Bearer <token>. Because a restarted server would invalidate the token, pass a fixed --token (or VM_MCP_TOKEN) in this mode.
VM_MCP_TOKEN=secret npx @violentmonkey/mcp -p 5678 --transport http
claude mcp add --transport http violentmonkey http://127.0.0.1:5678/mcp --header "Authorization: Bearer secret"Exposing the server beyond localhost (--host 0.0.0.0) is your responsibility; put it behind TLS and treat the token as a password. See docs/security.md.
| Tool | Description | Annotations | Violentmonkey version |
|---|---|---|---|
vm_status |
Whether the extension is connected, its version, and the connect URL. Works even when disconnected. | read-only | — |
scripts_list |
List scripts (id, name, namespace, version, enabled, matches). | read-only | >=2.49.5 |
scripts_get |
Get a script's metadata and source code by id. | read-only | >=2.49.5 |
scripts_write |
Create or update a script from source code. With id it updates that script; without id it matches by @name and @namespace, or creates a new one. Returns whether it was created. |
idempotent | >=2.49.5 |
scripts_set_enabled |
Enable or disable a script. | idempotent | >=2.49.5 |
scripts_delete |
Remove a script. | destructive | >=2.49.5 |
scripts_test_match |
Whether a script's source would be injected at a URL, answered by the extension's own matcher. | read-only | unreleased |
tabs_list |
Tabs the user has granted to the agent. Empty until they grant one. | read-only | unreleased |
tab_scripts |
What the extension attempted to run on a granted tab, including failures. | read-only | unreleased |
tab_reload |
Reload a granted tab so scripts are re-injected. Returns the new loadId. |
idempotent | unreleased |
script_logs |
Read GM_log output for a granted tab, paged by a seq cursor. |
read-only | unreleased |
— means the tool is handled by the server and needs no Violentmonkey support. unreleased means no Violentmonkey version implements the tool yet: the extension reports which tools it implements when it connects, and the server exposes only those, so an unimplemented tool is never listed — it does not appear and then fail.
The four tab tools (tabs_list, tab_scripts, tab_reload, script_logs) also need the user to grant a tab first. A grant is created explicitly in the Violentmonkey dashboard, lives in extension memory, and is revoked when its tab closes. Calling a tab tool on a tab that is not granted fails with 403. tab_reload is a plain reload, not navigation. See docs/contract.md for the full contract.
Exact input/output schemas live in packages/protocol and are the single source of truth.
In --readonly mode only read-only tools are exposed, which excludes scripts_write, scripts_set_enabled, scripts_delete and tab_reload.
Only tools the connected extension reports in its handshake are exposed. Before the extension connects, tools/list contains vm_status alone; the list is refreshed when the connection changes. Calling a tool that is not exposed returns an error result. A call that races a disconnect still fails with code 503 and the message Violentmonkey is not connected. Over the HTTP transport this is a tool result, not an HTTP status, since the HTTP request itself succeeded.
The server binds to localhost, requires a token, and only accepts connections from the browser extension. Use --readonly if the agent should only be able to read your scripts. See docs/security.md for the threat model.
- docs/design.md: how it works.
- docs/protocol.md: the extension WebSocket protocol.
- DEVELOPMENT.md: packages, building, testing, releasing, and integrating the client library into the extension.
MIT