@customaise/mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # @customaise/mcp
2
+
3
+ MCP server that connects AI coding agents to the [Customaise](https://customaise.com) Chrome extension for userscript management and browser automation.
4
+
5
+ ```
6
+ AI Agent ←(stdio)→ MCP Server ←(WebSocket)→ Customaise Extension
7
+ ```
8
+
9
+ ## Quick Start
10
+
11
+ ### 1. Install Customaise
12
+ Install the [Customaise Chrome extension](https://customaise.com) and enable **MCP Bridge** in Settings.
13
+
14
+ ### 2. Add to your IDE
15
+
16
+ **Cursor** (`.cursor/mcp.json`):
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "customaise": {
21
+ "command": "npx",
22
+ "args": ["-y", "@customaise/mcp"]
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ **Claude Desktop** (`claude_desktop_config.json`):
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "customaise": {
33
+ "command": "npx",
34
+ "args": ["-y", "@customaise/mcp"]
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ **Windsurf** (`.windsurf/mcp.json`):
41
+ ```json
42
+ {
43
+ "mcpServers": {
44
+ "customaise": {
45
+ "command": "npx",
46
+ "args": ["-y", "@customaise/mcp"]
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ **Antigravity** (`mcp_config.json`):
53
+ ```json
54
+ {
55
+ "mcpServers": {
56
+ "customaise": {
57
+ "command": "npx",
58
+ "args": ["-y", "@customaise/mcp"]
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ ### 3. Done!
65
+ Your AI agent can now manage userscripts, inspect browser pages, and take screenshots — all through natural language.
66
+
67
+ ## Available Tools (12)
68
+
69
+ ### Script Lifecycle
70
+ | Tool | Description |
71
+ |------|-------------|
72
+ | `list_scripts` | List all managed userscripts |
73
+ | `import_script` | Pull a script to a local file for editing |
74
+ | `export_script` | Push a local file to Customaise (validates + installs) |
75
+ | `delete_script` | Permanently delete a script |
76
+ | `toggle_script` | Enable or disable a script |
77
+
78
+ ### Browser Context
79
+ | Tool | Description |
80
+ |------|-------------|
81
+ | `get_page_context` | DOM snapshot of the current page |
82
+ | `get_console_context` | Console logs, errors, and GM_log output |
83
+ | `list_tabs` | List all open browser tabs |
84
+
85
+ ### Testing & Verification
86
+ | Tool | Description |
87
+ |------|-------------|
88
+ | `reload_tab` | Reload a tab to re-inject scripts |
89
+ | `take_screenshot` | Capture a screenshot of the visible tab |
90
+
91
+ ### UI Control
92
+ | Tool | Description |
93
+ |------|-------------|
94
+ | `toggle_ui` | Show or hide the Customaise UI overlay |
95
+
96
+ ### Batch Operations
97
+ | Tool | Description |
98
+ |------|-------------|
99
+ | `sync_scripts` | Bulk export all scripts to a local directory |
100
+
101
+ ## Typical Workflow
102
+
103
+ ```
104
+ 1. get_page_context → understand the target page
105
+ 2. write .user.js file → AI creates the script using IDE tools
106
+ 3. export_script → Customaise validates and installs
107
+ 4. reload_tab → re-inject the script
108
+ 5. get_console_context → check for errors
109
+ 6. take_screenshot → verify visual result
110
+ ```
111
+
112
+ ## File Sync
113
+
114
+ Use `sync_scripts` to bulk-export all scripts to a local directory:
115
+
116
+ ```
117
+ sync_scripts({ directory: "~/customaise-scripts" })
118
+ ```
119
+
120
+ This creates:
121
+ - **One `.user.js` file per script** — filename is derived from the script name (lowercase, hyphens, e.g. `my-cool-script.user.js`)
122
+ - **`.customaise-manifest.json`** — maps filenames to script IDs for round-trip editing
123
+
124
+ ### Manifest format
125
+
126
+ ```json
127
+ {
128
+ "dark-mode-fix.user.js": "vm_script_1774225715376_lus75sdzn",
129
+ "my-cool-script.user.js": "vm_script_1774225800123_abc12defg"
130
+ }
131
+ ```
132
+
133
+ ### Round-trip workflow
134
+
135
+ 1. `sync_scripts` → exports all scripts to a directory
136
+ 2. Edit any `.user.js` file in your IDE
137
+ 3. `export_script` with the file path + `scriptId` from the manifest → updates the script
138
+ 4. Omit `scriptId` when calling `export_script` → creates a new script instead
139
+
140
+ ### File watcher (auto-export)
141
+
142
+ When `sync_scripts` has been called, the MCP server watches the directory for `.user.js` changes. Saving a file in your IDE automatically pushes it to Customaise — no manual `export_script` call needed.
143
+
144
+ ## Configuration
145
+
146
+ | Environment Variable | Default | Description |
147
+ |---------------------|---------|-------------|
148
+ | `CUSTOMAISE_WS_PORT` | `4050` | WebSocket server port |
149
+
150
+ ## Requirements
151
+
152
+ - **Node.js** ≥ 18
153
+ - **Chrome** with the Customaise extension installed
154
+ - **MCP Bridge** enabled in Customaise Settings (Power User feature)
155
+
156
+ ## Troubleshooting
157
+
158
+ **"Customaise extension is not connected"**
159
+ - Make sure Chrome is running with the Customaise extension
160
+ - Check that MCP Bridge is enabled in extension Settings
161
+ - The extension connects automatically within a few seconds
162
+
163
+ **Port conflict on 4050**
164
+ - Set a different port: `CUSTOMAISE_WS_PORT=4051 npx @customaise/mcp`
165
+
166
+ **Scripts not running after export**
167
+ - Call `reload_tab` to trigger script re-injection
168
+ - Check `@match` pattern matches the current URL
169
+
170
+ ## License
171
+
172
+ MIT
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ExtensionBridge — WebSocket server that connects the MCP server to the
3
+ * Customaise Chrome extension.
4
+ *
5
+ * Architecture:
6
+ * MCP Server (stdio) ←→ ExtensionBridge (WS server :4050) ←→ Extension SW (WS client)
7
+ *
8
+ * The MCP Node process hosts the WebSocket server because MV3 service workers
9
+ * cannot run servers. The extension connects as a WebSocket client.
10
+ */
11
+ export declare class ExtensionBridge {
12
+ private wss;
13
+ private extensionSocket;
14
+ private pending;
15
+ private port;
16
+ private requestTimeoutMs;
17
+ constructor(port?: number, requestTimeoutMs?: number);
18
+ /**
19
+ * Start the WebSocket server and begin listening for the extension client.
20
+ */
21
+ start(): Promise<void>;
22
+ /**
23
+ * Send a request to the extension and wait for the response.
24
+ * Throws if the extension is not connected or the request times out.
25
+ */
26
+ request(type: string, args?: Record<string, unknown>): Promise<unknown>;
27
+ /**
28
+ * Whether the extension is currently connected.
29
+ */
30
+ get isConnected(): boolean;
31
+ /**
32
+ * Close the WebSocket server and all connections.
33
+ */
34
+ close(): Promise<void>;
35
+ /**
36
+ * Handle an incoming response from the extension, matching it to a pending request.
37
+ */
38
+ private _handleResponse;
39
+ /**
40
+ * Log to stderr (stdout is reserved for MCP stdio transport).
41
+ */
42
+ private _log;
43
+ }
44
+ //# sourceMappingURL=extension-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-bridge.d.ts","sourceRoot":"","sources":["../src/extension-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA0BH,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,gBAAgB,CAAS;gBAErB,IAAI,SAAO,EAAE,gBAAgB,SAAS;IAKlD;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA8CtB;;;OAGG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAsBjF;;OAEG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB5B;;OAEG;IACH,OAAO,CAAC,eAAe;IAiBvB;;OAEG;IACH,OAAO,CAAC,IAAI;CAGb"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * ExtensionBridge — WebSocket server that connects the MCP server to the
3
+ * Customaise Chrome extension.
4
+ *
5
+ * Architecture:
6
+ * MCP Server (stdio) ←→ ExtensionBridge (WS server :4050) ←→ Extension SW (WS client)
7
+ *
8
+ * The MCP Node process hosts the WebSocket server because MV3 service workers
9
+ * cannot run servers. The extension connects as a WebSocket client.
10
+ */
11
+ import { WebSocketServer, WebSocket } from 'ws';
12
+ import { randomUUID } from 'node:crypto';
13
+ export class ExtensionBridge {
14
+ wss = null;
15
+ extensionSocket = null;
16
+ pending = new Map();
17
+ port;
18
+ requestTimeoutMs;
19
+ constructor(port = 4050, requestTimeoutMs = 30_000) {
20
+ this.port = port;
21
+ this.requestTimeoutMs = requestTimeoutMs;
22
+ }
23
+ /**
24
+ * Start the WebSocket server and begin listening for the extension client.
25
+ */
26
+ start() {
27
+ return new Promise((resolve, reject) => {
28
+ this.wss = new WebSocketServer({ port: this.port }, () => {
29
+ this._log(`WebSocket server listening on ws://localhost:${this.port}`);
30
+ resolve();
31
+ });
32
+ this.wss.on('error', (err) => {
33
+ this._log(`WebSocket server error: ${err.message}`);
34
+ reject(err);
35
+ });
36
+ this.wss.on('connection', (ws) => {
37
+ this._log('Extension client connected');
38
+ // Only allow one client at a time (the Customaise extension)
39
+ if (this.extensionSocket) {
40
+ this._log('Replacing existing extension connection');
41
+ this.extensionSocket.close(1000, 'Replaced by new connection');
42
+ }
43
+ this.extensionSocket = ws;
44
+ ws.on('message', (data) => {
45
+ try {
46
+ const message = JSON.parse(data.toString());
47
+ this._handleResponse(message);
48
+ }
49
+ catch (err) {
50
+ this._log(`Failed to parse message from extension: ${err}`);
51
+ }
52
+ });
53
+ ws.on('close', (code, reason) => {
54
+ this._log(`Extension client disconnected (code=${code}, reason=${reason.toString()})`);
55
+ if (this.extensionSocket === ws) {
56
+ this.extensionSocket = null;
57
+ }
58
+ });
59
+ ws.on('error', (err) => {
60
+ this._log(`Extension client error: ${err.message}`);
61
+ });
62
+ });
63
+ });
64
+ }
65
+ /**
66
+ * Send a request to the extension and wait for the response.
67
+ * Throws if the extension is not connected or the request times out.
68
+ */
69
+ async request(type, args = {}) {
70
+ if (!this.extensionSocket || this.extensionSocket.readyState !== WebSocket.OPEN) {
71
+ throw new Error('Customaise extension is not connected. Make sure Chrome is running with the Customaise extension loaded.');
72
+ }
73
+ const id = randomUUID();
74
+ return new Promise((resolve, reject) => {
75
+ const timer = setTimeout(() => {
76
+ this.pending.delete(id);
77
+ reject(new Error(`Request to extension timed out after ${this.requestTimeoutMs}ms (type=${type}, id=${id})`));
78
+ }, this.requestTimeoutMs);
79
+ this.pending.set(id, { resolve, reject, timer });
80
+ const request = { id, type, args };
81
+ this.extensionSocket.send(JSON.stringify(request));
82
+ });
83
+ }
84
+ /**
85
+ * Whether the extension is currently connected.
86
+ */
87
+ get isConnected() {
88
+ return this.extensionSocket !== null && this.extensionSocket.readyState === WebSocket.OPEN;
89
+ }
90
+ /**
91
+ * Close the WebSocket server and all connections.
92
+ */
93
+ async close() {
94
+ // Reject all pending requests
95
+ for (const [id, pending] of this.pending) {
96
+ clearTimeout(pending.timer);
97
+ pending.reject(new Error('Bridge is shutting down'));
98
+ }
99
+ this.pending.clear();
100
+ if (this.extensionSocket) {
101
+ this.extensionSocket.close(1000, 'MCP server shutting down');
102
+ this.extensionSocket = null;
103
+ }
104
+ return new Promise((resolve) => {
105
+ if (this.wss) {
106
+ this.wss.close(() => {
107
+ this.wss = null;
108
+ resolve();
109
+ });
110
+ }
111
+ else {
112
+ resolve();
113
+ }
114
+ });
115
+ }
116
+ /**
117
+ * Handle an incoming response from the extension, matching it to a pending request.
118
+ */
119
+ _handleResponse(message) {
120
+ const pending = this.pending.get(message.id);
121
+ if (!pending) {
122
+ this._log(`Received response for unknown request id: ${message.id}`);
123
+ return;
124
+ }
125
+ this.pending.delete(message.id);
126
+ clearTimeout(pending.timer);
127
+ if (message.success) {
128
+ pending.resolve(message.result);
129
+ }
130
+ else {
131
+ pending.reject(new Error(message.error || 'Extension returned an error'));
132
+ }
133
+ }
134
+ /**
135
+ * Log to stderr (stdout is reserved for MCP stdio transport).
136
+ */
137
+ _log(message) {
138
+ process.stderr.write(`[customaise-mcp] ${message}\n`);
139
+ }
140
+ }
141
+ //# sourceMappingURL=extension-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-bridge.js","sourceRoot":"","sources":["../src/extension-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * FileWatcher — watches a synced scripts directory for .user.js changes
3
+ * and auto-exports modified files back to Customaise.
4
+ *
5
+ * Activated after sync_scripts creates the manifest.
6
+ * Uses native fs.watch() for reliable cross-platform file watching.
7
+ */
8
+ import { ExtensionBridge } from './extension-bridge.js';
9
+ export declare class FileWatcher {
10
+ private watcher;
11
+ private bridge;
12
+ private directory;
13
+ /** filenames we recently wrote — mute window to prevent re-export loops */
14
+ private muteSet;
15
+ private muteTimeoutMs;
16
+ /** debounce timers per file */
17
+ private debounceTimers;
18
+ private debounceMs;
19
+ constructor(bridge: ExtensionBridge);
20
+ /**
21
+ * Start watching a directory for .user.js changes.
22
+ * Replaces any previous watcher.
23
+ */
24
+ start(directory: string): void;
25
+ /**
26
+ * Mute a filename to prevent re-export after MCP-initiated writes.
27
+ */
28
+ muteFile(fileName: string): void;
29
+ /**
30
+ * Stop the file watcher.
31
+ */
32
+ stop(): void;
33
+ private handleFileChange;
34
+ private exportFile;
35
+ }
36
+ //# sourceMappingURL=file-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../src/file-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAIxD,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,SAAS,CAAc;IAE/B,2EAA2E;IAC3E,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,aAAa,CAAQ;IAE7B,+BAA+B;IAC/B,OAAO,CAAC,cAAc,CAAoD;IAC1E,OAAO,CAAC,UAAU,CAAO;gBAEb,MAAM,EAAE,eAAe;IAInC;;;OAGG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IA8B9B;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAOhC;;OAEG;IACH,IAAI,IAAI,IAAI;IAWZ,OAAO,CAAC,gBAAgB;YAqBV,UAAU;CAqCzB"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * FileWatcher — watches a synced scripts directory for .user.js changes
3
+ * and auto-exports modified files back to Customaise.
4
+ *
5
+ * Activated after sync_scripts creates the manifest.
6
+ * Uses native fs.watch() for reliable cross-platform file watching.
7
+ */
8
+ import { watch, readFileSync, existsSync } from 'node:fs';
9
+ import { basename, join } from 'node:path';
10
+ const LOG_PREFIX = '[customaise-mcp:watcher]';
11
+ export class FileWatcher {
12
+ watcher = null;
13
+ bridge;
14
+ directory = '';
15
+ /** filenames we recently wrote — mute window to prevent re-export loops */
16
+ muteSet = new Set();
17
+ muteTimeoutMs = 1000;
18
+ /** debounce timers per file */
19
+ debounceTimers = new Map();
20
+ debounceMs = 500;
21
+ constructor(bridge) {
22
+ this.bridge = bridge;
23
+ }
24
+ /**
25
+ * Start watching a directory for .user.js changes.
26
+ * Replaces any previous watcher.
27
+ */
28
+ start(directory) {
29
+ // Stop previous watcher if any
30
+ this.stop();
31
+ this.directory = directory;
32
+ const manifestPath = join(directory, '.customaise-manifest.json');
33
+ if (!existsSync(manifestPath)) {
34
+ console.error(`${LOG_PREFIX} No manifest found at ${manifestPath}. Run sync_scripts first.`);
35
+ return;
36
+ }
37
+ console.error(`${LOG_PREFIX} Watching ${directory} for .user.js changes`);
38
+ // Use native fs.watch on the directory
39
+ this.watcher = watch(directory, (eventType, filename) => {
40
+ if (!filename || !filename.endsWith('.user.js'))
41
+ return;
42
+ if (eventType === 'change' || eventType === 'rename') {
43
+ const filePath = join(directory, filename);
44
+ if (existsSync(filePath)) {
45
+ this.handleFileChange(filePath);
46
+ }
47
+ }
48
+ });
49
+ this.watcher.on('error', (error) => {
50
+ console.error(`${LOG_PREFIX} Watcher error:`, error.message);
51
+ });
52
+ }
53
+ /**
54
+ * Mute a filename to prevent re-export after MCP-initiated writes.
55
+ */
56
+ muteFile(fileName) {
57
+ this.muteSet.add(fileName);
58
+ setTimeout(() => {
59
+ this.muteSet.delete(fileName);
60
+ }, this.muteTimeoutMs);
61
+ }
62
+ /**
63
+ * Stop the file watcher.
64
+ */
65
+ stop() {
66
+ if (this.watcher) {
67
+ this.watcher.close();
68
+ this.watcher = null;
69
+ console.error(`${LOG_PREFIX} Watcher stopped`);
70
+ }
71
+ this.debounceTimers.forEach((timer) => clearTimeout(timer));
72
+ this.debounceTimers.clear();
73
+ this.muteSet.clear();
74
+ }
75
+ handleFileChange(filePath) {
76
+ const fileName = basename(filePath);
77
+ // Skip if this file is in the mute window (MCP-initiated write)
78
+ if (this.muteSet.has(fileName)) {
79
+ return;
80
+ }
81
+ // Debounce — IDEs may trigger multiple save events
82
+ const existing = this.debounceTimers.get(fileName);
83
+ if (existing)
84
+ clearTimeout(existing);
85
+ this.debounceTimers.set(fileName, setTimeout(() => {
86
+ this.debounceTimers.delete(fileName);
87
+ this.exportFile(filePath, fileName);
88
+ }, this.debounceMs));
89
+ }
90
+ async exportFile(filePath, fileName) {
91
+ try {
92
+ // Read manifest to find scriptId
93
+ const manifestPath = join(this.directory, '.customaise-manifest.json');
94
+ if (!existsSync(manifestPath))
95
+ return;
96
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
97
+ const scriptId = manifest[fileName];
98
+ if (!scriptId) {
99
+ console.error(`${LOG_PREFIX} No scriptId in manifest for ${fileName} — skipping`);
100
+ return;
101
+ }
102
+ // Read file content
103
+ const code = readFileSync(filePath, 'utf-8');
104
+ if (!code.trim())
105
+ return;
106
+ console.error(`${LOG_PREFIX} Auto-exporting ${fileName} → ${scriptId}`);
107
+ // Send to extension via bridge
108
+ const result = await this.bridge.request('export_script', {
109
+ code,
110
+ scriptId,
111
+ });
112
+ if (result?.success) {
113
+ console.error(`${LOG_PREFIX} ✓ ${fileName} exported successfully`);
114
+ }
115
+ else {
116
+ console.error(`${LOG_PREFIX} ✗ ${fileName} export failed: ${result?.error || 'unknown'}`);
117
+ }
118
+ }
119
+ catch (error) {
120
+ console.error(`${LOG_PREFIX} Error exporting ${fileName}:`, error instanceof Error ? error.message : error);
121
+ }
122
+ }
123
+ }
124
+ //# sourceMappingURL=file-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-watcher.js","sourceRoot":"","sources":["../src/file-watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Customaise MCP Server — Entry Point
4
+ *
5
+ * Connects AI coding agents (Cursor, Antigravity, Claude Code) to the
6
+ * Customaise Chrome extension via the Model Context Protocol.
7
+ *
8
+ * Transport:
9
+ * AI Agent ←(stdio)→ MCP Server ←(WebSocket)→ Chrome Extension
10
+ *
11
+ * Usage:
12
+ * node dist/index.js
13
+ *
14
+ * Environment:
15
+ * CUSTOMAISE_WS_PORT — WebSocket server port (default: 4050)
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;GAcG"}
package/dist/index.js ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Customaise MCP Server — Entry Point
4
+ *
5
+ * Connects AI coding agents (Cursor, Antigravity, Claude Code) to the
6
+ * Customaise Chrome extension via the Model Context Protocol.
7
+ *
8
+ * Transport:
9
+ * AI Agent ←(stdio)→ MCP Server ←(WebSocket)→ Chrome Extension
10
+ *
11
+ * Usage:
12
+ * node dist/index.js
13
+ *
14
+ * Environment:
15
+ * CUSTOMAISE_WS_PORT — WebSocket server port (default: 4050)
16
+ */
17
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
18
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
19
+ import { registerTools, registerPromptsAndResources } from './server.js';
20
+ import { ExtensionBridge } from './extension-bridge.js';
21
+ import { FileWatcher } from './file-watcher.js';
22
+ const WS_PORT = Number(process.env.CUSTOMAISE_WS_PORT || process.env.VIBEMONKEY_WS_PORT) || 4050;
23
+ async function main() {
24
+ // 1. Start the WebSocket server for the extension to connect to
25
+ const bridge = new ExtensionBridge(WS_PORT);
26
+ await bridge.start();
27
+ // 2. Create the MCP server
28
+ const server = new McpServer({
29
+ name: 'customaise',
30
+ version: '1.0.0'
31
+ }, {
32
+ capabilities: {
33
+ tools: {},
34
+ prompts: {},
35
+ resources: {}
36
+ }
37
+ });
38
+ // 3. Create file watcher for sync_scripts auto-export
39
+ const fileWatcher = new FileWatcher(bridge);
40
+ // 4. Register tools, prompts, and resources
41
+ registerTools(server, bridge, fileWatcher);
42
+ registerPromptsAndResources(server, bridge);
43
+ // 4. Connect via stdio transport (AI agent communicates over stdin/stdout)
44
+ const transport = new StdioServerTransport();
45
+ await server.connect(transport);
46
+ process.stderr.write('[customaise-mcp] MCP server running (stdio + WebSocket)\n');
47
+ // Graceful shutdown
48
+ const shutdown = async () => {
49
+ process.stderr.write('[customaise-mcp] Shutting down...\n');
50
+ fileWatcher.stop();
51
+ await bridge.close();
52
+ await server.close();
53
+ process.exit(0);
54
+ };
55
+ process.on('SIGINT', shutdown);
56
+ process.on('SIGTERM', shutdown);
57
+ }
58
+ main().catch((err) => {
59
+ process.stderr.write(`[customaise-mcp] Fatal error: ${err.message}\n`);
60
+ process.exit(1);
61
+ });
62
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,IAAI,CAAC;AAEjG,KAAK,UAAU,IAAI;IACjB,gEAAgE;IAChE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAErB,2BAA2B;IAC3B,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;QACE,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,OAAO;KACjB,EACD;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE;YACT,OAAO,EAAE,EAAE;YACX,SAAS,EAAE,EAAE;SACd;KACF,CACF,CAAC;IAEF,sDAAsD;IACtD,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IAE5C,4CAA4C;IAC5C,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3C,2BAA2B,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE5C,2EAA2E;IAC3E,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAElF,oBAAoB;IACpB,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QAC5D,WAAW,CAAC,IAAI,EAAE,CAAC;QACnB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;IACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { ExtensionBridge } from './extension-bridge.js';
3
+ import { FileWatcher } from './file-watcher.js';
4
+ /**
5
+ * Register all MCP tools with the server.
6
+ */
7
+ export declare function registerTools(server: McpServer, bridge: ExtensionBridge, fileWatcher?: FileWatcher): void;
8
+ /**
9
+ * Register MCP Prompts and Resources.
10
+ */
11
+ export declare function registerPromptsAndResources(server: McpServer, bridge: ExtensionBridge): void;
12
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAKtF,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CA0UzG;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAqJ5F"}
package/dist/server.js ADDED
@@ -0,0 +1,472 @@
1
+ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
6
+ /**
7
+ * Register all MCP tools with the server.
8
+ */
9
+ export function registerTools(server, bridge, fileWatcher) {
10
+ // ─── Script Lifecycle ───────────────────────────────────────────────
11
+ server.tool('list_scripts', 'List all userscripts managed by Customaise. Returns each script\'s ID, name, enabled status, URL match patterns, and description. Use this to discover available scripts before importing or modifying them.', {}, async () => {
12
+ const result = await bridge.request('list_scripts', {});
13
+ return {
14
+ content: [{
15
+ type: 'text',
16
+ text: JSON.stringify(result, null, 2)
17
+ }]
18
+ };
19
+ });
20
+ server.tool('import_script', 'Import an existing userscript from Customaise to a local file for editing. The file will include the full source code with metadata block. After editing the file with your IDE tools, use export_script to push changes back to Customaise.', {
21
+ scriptId: z.string().describe('The ID of the script to import (get from list_scripts)'),
22
+ filePath: z.string().describe('Local file path to write the script to (e.g., ./scripts/my-script.user.js)')
23
+ }, async ({ scriptId, filePath }) => {
24
+ const result = await bridge.request('import_script', { scriptId });
25
+ // Ensure directory exists and write file
26
+ mkdirSync(dirname(filePath), { recursive: true });
27
+ writeFileSync(filePath, result.source, 'utf-8');
28
+ return {
29
+ content: [{
30
+ type: 'text',
31
+ text: JSON.stringify({
32
+ success: true,
33
+ filePath,
34
+ scriptId: result.scriptId,
35
+ metadata: result.metadata,
36
+ bytesWritten: Buffer.byteLength(result.source, 'utf-8')
37
+ }, null, 2)
38
+ }]
39
+ };
40
+ });
41
+ server.tool('export_script', 'Export a userscript from a local file into Customaise. The file will be validated through Customaise\'s sanitization pipeline (syntax checking, AST validation, security analysis). If valid, the script is installed and ready to execute on matching pages. If invalid, detailed diagnostics explain exactly what to fix. Pass scriptId to update an existing script instead of creating a new one.', {
42
+ filePath: z.string().describe('Local file path containing the userscript source code'),
43
+ scriptId: z.string().optional().describe('ID of an existing script to update. Omit to create a new script.')
44
+ }, async ({ filePath, scriptId }) => {
45
+ const code = readFileSync(filePath, 'utf-8');
46
+ const result = await bridge.request('export_script', { code, scriptId });
47
+ return {
48
+ content: [{
49
+ type: 'text',
50
+ text: JSON.stringify(result, null, 2)
51
+ }]
52
+ };
53
+ });
54
+ server.tool('delete_script', 'Permanently delete a userscript from Customaise. This action cannot be undone.', {
55
+ scriptId: z.string().describe('The ID of the script to delete')
56
+ }, async ({ scriptId }) => {
57
+ const result = await bridge.request('delete_script', { scriptId });
58
+ return {
59
+ content: [{
60
+ type: 'text',
61
+ text: JSON.stringify(result, null, 2)
62
+ }]
63
+ };
64
+ });
65
+ server.tool('toggle_script', 'Enable or disable a userscript. Disabled scripts are not injected into matching pages. Use this to temporarily turn off a script without deleting it.', {
66
+ scriptId: z.string().describe('The ID of the script to enable/disable'),
67
+ enabled: z.boolean().describe('true to enable, false to disable')
68
+ }, async ({ scriptId, enabled }) => {
69
+ const result = await bridge.request('set_script_enabled', { scriptId, enabled });
70
+ return {
71
+ content: [{
72
+ type: 'text',
73
+ text: JSON.stringify(result, null, 2)
74
+ }]
75
+ };
76
+ });
77
+ // ─── Browser Context ────────────────────────────────────────────────
78
+ server.tool('get_page_context', 'Get a DOM snapshot of the current page including URL, title, page structure, and visible elements. Use this to understand the page layout before writing userscripts that manipulate it.', {
79
+ tabId: z.number().optional().describe('Tab ID to inspect. Defaults to the active tab.')
80
+ }, async ({ tabId }) => {
81
+ const result = await bridge.request('get_page_context', { tabId });
82
+ return {
83
+ content: [{
84
+ type: 'text',
85
+ text: JSON.stringify(result, null, 2)
86
+ }]
87
+ };
88
+ });
89
+ server.tool('get_console_context', 'Get console logs from the browser, including errors, warnings, and userscript GM_log output. Use after reload_tab to check for script runtime errors. Logs include userscript-specific entries by default.', {
90
+ tabId: z.number().optional().describe('Tab ID to get logs from. Defaults to the active tab.'),
91
+ level: z.enum(['all', 'error', 'warn', 'info', 'debug']).optional().describe('Filter by log level. Default: all')
92
+ }, async ({ tabId, level }) => {
93
+ const result = await bridge.request('get_console_context', { tabId });
94
+ // Apply level filter client-side — ConsoleContextService returns
95
+ // pre-structured arrays (errors, warnings, userscriptLogs).
96
+ if (level && level !== 'all') {
97
+ const filtered = { ...result };
98
+ if (level === 'error') {
99
+ delete filtered.warnings;
100
+ delete filtered.userscriptLogs;
101
+ }
102
+ else if (level === 'warn') {
103
+ delete filtered.errors;
104
+ delete filtered.userscriptLogs;
105
+ }
106
+ else if (level === 'info' || level === 'debug') {
107
+ delete filtered.errors;
108
+ delete filtered.warnings;
109
+ }
110
+ return {
111
+ content: [{
112
+ type: 'text',
113
+ text: JSON.stringify(filtered, null, 2)
114
+ }]
115
+ };
116
+ }
117
+ return {
118
+ content: [{
119
+ type: 'text',
120
+ text: JSON.stringify(result, null, 2)
121
+ }]
122
+ };
123
+ });
124
+ server.tool('list_tabs', 'List all open browser tabs with their IDs, URLs, titles, and active status. Use to find a specific tab ID for other tools like reload_tab or take_screenshot.', {}, async () => {
125
+ const result = await bridge.request('list_tabs', {});
126
+ return {
127
+ content: [{
128
+ type: 'text',
129
+ text: JSON.stringify(result, null, 2)
130
+ }]
131
+ };
132
+ });
133
+ server.tool('reload_tab', 'Reload a browser tab to re-inject updated userscripts. Use after export_script to see the effect of your changes. Waits for the page to fully load before returning.', {
134
+ tabId: z.number().optional().describe('Tab ID to reload. Defaults to the active tab.')
135
+ }, async ({ tabId }) => {
136
+ const result = await bridge.request('reload_tab', { tabId });
137
+ return {
138
+ content: [{
139
+ type: 'text',
140
+ text: JSON.stringify(result, null, 2)
141
+ }]
142
+ };
143
+ });
144
+ server.tool('take_screenshot', 'Capture a screenshot of the current visible browser tab. Use to verify visual changes made by userscripts. The screenshot is saved as a PNG file.', {
145
+ tabId: z.number().optional().describe('Tab ID to screenshot. Defaults to the active tab.'),
146
+ filePath: z.string().optional().describe('Local file path to save the screenshot. Auto-generates a temp path if omitted.')
147
+ }, async ({ tabId, filePath }) => {
148
+ const result = await bridge.request('take_screenshot', { tabId });
149
+ // Write base64 PNG data to file
150
+ const savePath = filePath || join(tmpdir(), `customaise-screenshot-${Date.now()}.png`);
151
+ const base64Data = result.dataUrl.replace(/^data:image\/png;base64,/, '');
152
+ mkdirSync(dirname(savePath), { recursive: true });
153
+ writeFileSync(savePath, Buffer.from(base64Data, 'base64'));
154
+ return {
155
+ content: [{
156
+ type: 'text',
157
+ text: JSON.stringify({
158
+ success: true,
159
+ filePath: savePath,
160
+ width: result.width,
161
+ height: result.height
162
+ }, null, 2)
163
+ }]
164
+ };
165
+ });
166
+ server.tool('toggle_ui', 'Show or hide the Customaise UI overlay on the active tab. Use this to make the Customaise interface visible or dismiss it — AI agents cannot click the extension icon directly. Optionally specify which panel to open.', {
167
+ tabId: z.number().optional().describe('Tab ID to toggle UI on. Defaults to the active tab.'),
168
+ panel: z.string().optional().describe('Panel to open: "scripts", "chat", "settings"')
169
+ }, async ({ tabId, panel }) => {
170
+ const result = await bridge.request('show_ui', { tabId, panel });
171
+ return {
172
+ content: [{
173
+ type: 'text',
174
+ text: JSON.stringify(result, null, 2)
175
+ }]
176
+ };
177
+ });
178
+ // ─── File Sync ──────────────────────────────────────────────────────
179
+ server.tool('sync_scripts', 'Bulk export all scripts from Customaise to a local directory as individual .user.js files. Creates a .customaise-manifest.json mapping filenames to script IDs. Use this to set up a local workspace for editing scripts with your IDE.', {
180
+ directory: z.string().describe('Local directory to export scripts to (e.g., ./customaise-scripts/)')
181
+ }, async ({ directory }) => {
182
+ // Get all scripts with code
183
+ const scripts = await bridge.request('list_scripts_with_code', {});
184
+ mkdirSync(directory, { recursive: true });
185
+ const manifest = {};
186
+ let filesWritten = 0;
187
+ const usedNames = new Set();
188
+ for (const script of scripts) {
189
+ // Generate a safe filename from the script name
190
+ let safeName = (script.name || 'untitled')
191
+ .toLowerCase()
192
+ .replace(/[^a-z0-9_-]/g, '-')
193
+ .replace(/-+/g, '-')
194
+ .replace(/^-|-$/g, '');
195
+ // Handle filename collisions — append short ID suffix if name already used
196
+ let fileName = `${safeName}.user.js`;
197
+ if (usedNames.has(fileName)) {
198
+ const idSuffix = script.id.slice(-6);
199
+ fileName = `${safeName}-${idSuffix}.user.js`;
200
+ }
201
+ usedNames.add(fileName);
202
+ const filePath = `${directory}/${fileName}`;
203
+ // Mute each file to prevent the watcher from re-exporting
204
+ if (fileWatcher)
205
+ fileWatcher.muteFile(fileName);
206
+ writeFileSync(filePath, script.code || '', 'utf-8');
207
+ manifest[fileName] = script.id;
208
+ filesWritten++;
209
+ }
210
+ // Write manifest for ID mapping
211
+ const manifestPath = `${directory}/.customaise-manifest.json`;
212
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8');
213
+ // Start file watcher on the synced directory
214
+ if (fileWatcher) {
215
+ fileWatcher.start(directory);
216
+ }
217
+ return {
218
+ content: [{
219
+ type: 'text',
220
+ text: JSON.stringify({
221
+ success: true,
222
+ directory,
223
+ filesWritten,
224
+ manifestPath,
225
+ scripts: Object.entries(manifest).map(([file, id]) => ({ file, id }))
226
+ }, null, 2)
227
+ }]
228
+ };
229
+ });
230
+ }
231
+ /**
232
+ * Register MCP Prompts and Resources.
233
+ */
234
+ export function registerPromptsAndResources(server, bridge) {
235
+ // ─── Prompts ────────────────────────────────────────────────────────
236
+ server.prompt('create-userscript', 'Guided workflow for creating a new userscript. Provides a step-by-step prompt that helps the AI agent understand what the user needs and produce a working script.', {
237
+ targetUrl: z.string().describe('The URL pattern the script should match (e.g., *://*.example.com/*)'),
238
+ goal: z.string().describe('What the script should accomplish')
239
+ }, async ({ targetUrl, goal }) => {
240
+ return {
241
+ messages: [
242
+ {
243
+ role: 'user',
244
+ content: {
245
+ type: 'text',
246
+ text: [
247
+ `Create a userscript for Customaise that does the following:`,
248
+ ``,
249
+ `**Target URL:** ${targetUrl}`,
250
+ `**Goal:** ${goal}`,
251
+ ``,
252
+ `## Requirements`,
253
+ `1. Include a proper metadata block with @name, @match, @description, @version, and @grant directives`,
254
+ `2. Use @match ${targetUrl}`,
255
+ `3. Wrap the script in an IIFE containing named functions (required for symbol-level editing via Customaise)`,
256
+ `4. If making cross-origin requests, include the @connect directive`,
257
+ `5. Use GM_log for debug output (it appears in Customaise's console context)`,
258
+ `6. Handle edge cases (element not found, page still loading, etc.)`,
259
+ ``,
260
+ `## Workflow`,
261
+ `1. Use \`get_page_context\` to understand the page structure`,
262
+ `2. Write the script code`,
263
+ `3. For targeting existing page elements, consider using \`VM_findElement\` with \`dom_*\` IDs from \`get_page_context\` for bulletproof selector resilience`,
264
+ `4. Use \`export_script\` to install it in Customaise (it will be validated through the sanitization pipeline)`,
265
+ `5. Use \`reload_tab\` to test it`,
266
+ `6. Use \`get_console_context\` to check for errors or GM_log output`,
267
+ `7. If there are issues, fix and re-export`,
268
+ ].join('\n')
269
+ }
270
+ }
271
+ ]
272
+ };
273
+ });
274
+ server.prompt('debug-userscript', 'Debugging workflow for an existing userscript that isn\'t working as expected. Guides the AI through systematic diagnosis using available tools.', {
275
+ scriptId: z.string().describe('The ID of the script to debug')
276
+ }, async ({ scriptId }) => {
277
+ return {
278
+ messages: [
279
+ {
280
+ role: 'user',
281
+ content: {
282
+ type: 'text',
283
+ text: [
284
+ `Debug the userscript with ID \`${scriptId}\`.`,
285
+ ``,
286
+ `## Debugging Steps`,
287
+ `1. Use \`import_script\` to pull the script to a local file for inspection`,
288
+ `2. Check if the script is enabled with \`list_scripts\``,
289
+ `3. Use \`list_tabs\` to find a tab matching the script's @match pattern`,
290
+ `4. Use \`reload_tab\` on that tab to trigger script injection`,
291
+ `5. Use \`get_console_context\` to capture errors and GM_log output`,
292
+ `6. Use \`get_page_context\` to verify the DOM state`,
293
+ `7. If needed, use \`take_screenshot\` to see the visual result`,
294
+ ``,
295
+ `## Common Issues`,
296
+ `- @match pattern doesn't match the current URL`,
297
+ `- @run-at timing: script runs before target elements exist`,
298
+ `- Missing @grant for GM_* or VM_* APIs used in the script`,
299
+ `- Missing @connect directive for cross-origin GM_xmlhttpRequest calls`,
300
+ `- CSP blocking inline script injection`,
301
+ `- Element selectors changed on the page`,
302
+ ``,
303
+ `Fix any issues found and use \`export_script\` to push updates.`,
304
+ ].join('\n')
305
+ }
306
+ }
307
+ ]
308
+ };
309
+ });
310
+ // ─── Resources ──────────────────────────────────────────────────────
311
+ server.resource('scripts-list', 'customaise://scripts', {
312
+ description: 'Live list of all userscripts managed by Customaise, including their IDs, names, and enabled status.',
313
+ mimeType: 'application/json'
314
+ }, async (uri) => {
315
+ const result = await bridge.request('list_scripts', {});
316
+ return {
317
+ contents: [{
318
+ uri: uri.href,
319
+ mimeType: 'application/json',
320
+ text: JSON.stringify(result, null, 2)
321
+ }]
322
+ };
323
+ });
324
+ server.resource('script-source', new ResourceTemplate('customaise://scripts/{scriptId}', { list: undefined }), {
325
+ description: 'Full source code and metadata of a specific userscript. Use the script ID from the scripts list.',
326
+ mimeType: 'application/json'
327
+ }, async (uri, variables) => {
328
+ const scriptId = variables.scriptId;
329
+ // Reuse the import_script bridge command (same logic, avoids duplication)
330
+ const result = await bridge.request('import_script', { scriptId });
331
+ return {
332
+ contents: [{
333
+ uri: uri.href,
334
+ mimeType: 'application/json',
335
+ text: JSON.stringify(result, null, 2)
336
+ }]
337
+ };
338
+ });
339
+ server.resource('conventions', 'customaise://conventions', {
340
+ description: 'Userscript writing conventions and best practices for Customaise.',
341
+ mimeType: 'text/markdown'
342
+ }, async (uri) => {
343
+ return {
344
+ contents: [{
345
+ uri: uri.href,
346
+ mimeType: 'text/markdown',
347
+ text: CONVENTIONS_GUIDE
348
+ }]
349
+ };
350
+ });
351
+ }
352
+ const CONVENTIONS_GUIDE = `# Customaise Userscript Conventions
353
+
354
+ ## File Format & Structure
355
+
356
+ Every userscript is a single \`.user.js\` file with a metadata block at the top.
357
+ **CRITICAL**: Customaise supports symbol-level editing (function-by-function). To enable this, your script MUST be wrapped in an IIFE containing **named functions**, rather than flat inline code.
358
+
359
+ \`\`\`javascript
360
+ // ==UserScript==
361
+ // @name My Script
362
+ // @description What this script does
363
+ // @match https://example.com/*
364
+ // @version 1.0
365
+ // @grant GM_log
366
+ // @grant VM_findElement
367
+ // @run-at document-idle
368
+ // ==/UserScript==
369
+
370
+ (function() {
371
+ 'use strict';
372
+
373
+ // Good: Named functions allow Customaise to edit them individually
374
+ async function init() {
375
+ GM_log('Script initialized');
376
+ await hideAnnoyingBanner();
377
+ }
378
+
379
+ async function hideAnnoyingBanner() {
380
+ // VM_findElement is our bulletproof DOM selector
381
+ const banner = await VM_findElement('dom_banner_123');
382
+ if (banner) banner.style.display = 'none';
383
+ }
384
+
385
+ init();
386
+ })();
387
+ \`\`\`
388
+
389
+ ## Metadata Directives
390
+
391
+ | Directive | Required | Description |
392
+ |-----------|----------|-------------|
393
+ | \`@name\` | ✅ | Script name (must be unique) |
394
+ | \`@match\` | ✅ | URL pattern(s) where the script runs (\`*://*.example.com/*\`) |
395
+ | \`@description\` | Recommended | What the script does |
396
+ | \`@version\` | Recommended | Semantic version (defaults to 1.0) |
397
+ | \`@grant\` | Optional | GM_* or VM_* APIs to enable (use \`none\` for no special APIs) |
398
+ | \`@run-at\` | Optional | When to inject: \`document-start\`, \`document-end\`, \`document-idle\` (default) |
399
+ | \`@connect\` | Optional | Domains allowed for \`GM_xmlhttpRequest\` (e.g., \`api.github.com\`) |
400
+ | \`@domId\` | Auto | Auto-managed by Customaise for \`VM_findElement\`. **Do not edit manually.** |
401
+ | \`@require\` | Optional | External JS libraries to load before the script |
402
+ | \`@resource\` | Optional | Named external resources (CSS, JSON, images) accessible via \`GM_getResourceText/URL\` |
403
+ | \`@namespace\` | Optional | Script namespace (used for de-duplication with imported scripts) |
404
+ | \`@author\` | Optional | Script author |
405
+
406
+ ## VM_findElement (Bulletproof DOM Targeting)
407
+
408
+ Customaise provides a revolutionary multi-tier selector API that guarantees 100% element targeting reliability, surviving UI redesigns and dynamic class changes.
409
+
410
+ **Usage:**
411
+ 1. You must declare \`@grant VM_findElement\`
412
+ 2. Pass a \`dom_*\` ID string (e.g., \`await VM_findElement('dom_1234567890_abc')\`)
413
+ 3. **Important:** \`dom_*\` IDs are generated by the user using the Customaise DOM selector tooltip. Do not invent your own \`dom_*\` IDs. If creating elements dynamically, use standard \`document.querySelector\`.
414
+ 4. The function is async and must be awaited.
415
+
416
+ **VM_findExternalElement:** Works like \`VM_findElement\` but targets elements inside cross-origin iframes. Requires \`@connect\` for the iframe's domain. Usage: \`await VM_findExternalElement('dom_ext_xxx')\`.
417
+
418
+ ## Available GM_* APIs
419
+
420
+ Customaise supports 22 \`GM_*\` APIs, making it highly compatible with existing Greasemonkey/Tampermonkey scripts. You can use either the classic \`GM_*\` (underscore) or modern \`GM.*\` (promise-based) syntax.
421
+
422
+ ### Environment & Console
423
+ | API | Description |
424
+ |-----|-------------|
425
+ | \`GM_log(msg)\` / \`GM.log(msg)\` | Log to Customaise console (visible via \`get_console_context\` tool) |
426
+ | \`GM_info\` / \`GM.info\` | Object containing script metadata |
427
+
428
+ ### Storage (Extension-Scoped)
429
+ | API | Description |
430
+ |-----|-------------|
431
+ | \`GM_setValue(k, v)\` / \`GM.setValue(k, v)\` | Persistent storage (survives page reloads) |
432
+ | \`GM_getValue(k, def)\` / \`GM.getValue(k, def)\` | Read from persistent storage |
433
+ | \`GM_deleteValue(k)\` / \`GM.deleteValue(k)\` | Delete from persistent storage |
434
+ | \`GM_listValues()\` / \`GM.listValues()\` | List all stored keys |
435
+ | \`GM_addValueChangeListener(name, cb)\` | Listen for storage changes across tabs |
436
+ | \`GM_removeValueChangeListener(id)\` | Remove storage listener |
437
+
438
+ ### DOM & UI
439
+ | API | Description |
440
+ |-----|-------------|
441
+ | \`GM_addStyle(css)\` / \`GM.addStyle(css)\` | Inject CSS into the page |
442
+ | \`GM_addElement(tag, attr)\` / \`GM.addElement(tag, attr)\` | Safely create and append DOM elements |
443
+ | \`GM_registerMenuCommand(name, fn)\` | Add a command to the Customaise extension menu |
444
+ | \`GM_unregisterMenuCommand(id)\` | Remove a menu command |
445
+ | \`GM_notification(details)\` / \`GM.notification(details)\` | Show a desktop OS notification |
446
+
447
+ ### Network & Resources
448
+ | API | Description |
449
+ |-----|-------------|
450
+ | \`GM_xmlhttpRequest(details)\` | Cross-origin HTTP requests. **Requires \`@connect\` directive.** |
451
+ | \`GM.xmlHttpRequest(details)\` | Promise-based cross-origin HTTP requests |
452
+ | \`GM_download(details)\` | Download a file to disk |
453
+ | \`GM_getResourceText(name)\` / \`GM.getResourceText(name)\` | Read text content from a \`@resource\` |
454
+ | \`GM_getResourceURL(name)\` / \`GM.getResourceUrl(name)\` | Get base64 data URI for a \`@resource\` |
455
+
456
+ ### Tabs & System
457
+ | API | Description |
458
+ |-----|-------------|
459
+ | \`GM_setClipboard(text)\` / \`GM.setClipboard(text)\` | Copy text to OS clipboard |
460
+ | \`GM_openInTab(url, options)\` / \`GM.openInTab(url, options)\` | Open a new browser tab |
461
+ | \`GM_getTab(cb)\` / \`GM.getTab()\` | Get persistent state for the current tab |
462
+ | \`GM_saveTab(obj)\` / \`GM.saveTab(obj)\` | Save persistent state for the current tab |
463
+ | \`GM_getTabs(cb)\` / \`GM.getTabs()\` | Get persistent state for all tabs running this script |
464
+
465
+ ## Developer Workflow & Best Practices
466
+
467
+ 1. **Use \`GM_log\` over \`console.log\`:** \`GM_log\` output is explicitly tracked by Customaise and is visible when using the \`get_console_context\` MCP tool.
468
+ 2. **Cross-Origin Requests:** If your script needs to fetch data from \`api.github.com\`, you MUST include \`// @connect api.github.com\` in the metadata block, or \`GM_xmlhttpRequest\` will fail silently.
469
+ 3. **Handle Dynamic Pages:** Most modern sites are SPAs (Single Page Applications). Elements may not exist immediately. Use \`VM_findElement\` or \`MutationObserver\` instead of assuming elements are present on load.
470
+ 4. **Execution Timing:** \`// @run-at document-idle\` is the safest default as it ensures the initial DOM is fully parsed.
471
+ `;
472
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@customaise/mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server that connects AI coding agents (Cursor, Claude Code, Windsurf, Antigravity) to the Customaise Chrome extension for userscript management and browser automation.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "customaise-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "start": "node dist/index.js",
17
+ "dev": "node --loader ts-node/esm src/index.ts",
18
+ "test": "tsc -p tsconfig.test.json && node --loader ./test-loader.mjs --test test-out/__tests__/*.test.js",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "customaise",
24
+ "userscripts",
25
+ "chrome-extension",
26
+ "browser-automation",
27
+ "model-context-protocol",
28
+ "ai-tools",
29
+ "cursor",
30
+ "claude",
31
+ "windsurf"
32
+ ],
33
+ "author": "Customaise",
34
+ "license": "MIT",
35
+ "homepage": "https://customaise.com",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/customaise/customaise-mcp"
39
+ },
40
+ "engines": {
41
+ "node": ">=18.0.0"
42
+ },
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.26.0",
45
+ "ws": "^8.18.0",
46
+ "zod": "^3.24.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^22.0.0",
50
+ "@types/ws": "^8.5.0",
51
+ "ts-node": "^10.9.0",
52
+ "tsx": "^4.21.0",
53
+ "typescript": "^5.7.0",
54
+ "vitest": "^4.1.0"
55
+ }
56
+ }