Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ itself rather than reconstructing it at tag time.

### Fixed

- **DXT settings now reach the server.** The Claude Desktop DXT passed its
WebSocket Port, HTTP Port and Auto-Tunnel settings as env variables the server
never read, so changing them did nothing. The server now reads
`OPENDIA_WS_PORT`, `OPENDIA_HTTP_PORT`, `OPENDIA_ENABLE_TUNNEL` and
`OPENDIA_TOKEN` (flags still win), and the DXT maps its settings onto them. The
DXT gains an optional Tunnel Auth Token setting and drops its Safety Mode
setting, which had no effect; Safety Mode stays a toggle in the extension popup.
The popup also gets a Custom HTTP Port field, checked first during server
discovery, so the extension can find a server moved off the usual ports. (#85)

- **Tools no longer report failures to the model as success.** `get_history`
rendered a browser API failure as "No history items found", `get_selected_text`
rendered four distinct failures as "No text selected", `page_navigate` claimed
Expand Down
26 changes: 24 additions & 2 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ Wired it up with something else? Open a PR — the list grows as MCP grows.
2. Double-click the `.dxt` file to install automatically
3. The MCP will be added to your Claude Desktop configuration

The DXT settings in Claude Desktop (WebSocket Port, HTTP Port, Auto-Tunnel,
Tunnel Auth Token) are passed to the server as the `OPENDIA_*` environment
variables described under [Port Configuration](#port-configuration). If you move
the HTTP port outside the ports the extension checks (5556-5558, 3001,
6001-6003), enter it under **Custom HTTP Port** in the extension popup. Safety
Mode is a toggle in the extension popup, not a DXT setting.

**Option 2: Manual Configuration**
Add to your Claude Desktop configuration:
```json
Expand Down Expand Up @@ -209,6 +216,21 @@ npx opendia --ws-port=5555 --http-port=5556 # Specify individually
# Note: Existing OpenDia processes are automatically terminated on startup
```

The same settings can come from environment variables, which is how the Claude
Desktop DXT applies its settings. A command-line flag always wins over the
matching variable, and an empty variable counts as unset.

| Variable | Same as | Notes |
| --- | --- | --- |
| `OPENDIA_WS_PORT` | `--ws-port=` | Integer 1-65535 |
| `OPENDIA_HTTP_PORT` | `--http-port=` | Integer 1-65535 |
| `OPENDIA_ENABLE_TUNNEL` | `--tunnel` | `1`, `true` or `yes` turns it on |
| `OPENDIA_TOKEN` | `--token=` | Bearer token for tunnel mode |

The browser extension finds the server by checking HTTP ports 5556-5558, 3001
and 6001-6003. For any other HTTP port, enter it under **Custom HTTP Port** in
the extension popup so it is checked first.

### Auto-Tunnel Mode
```bash
npx opendia --tunnel
Expand All @@ -218,8 +240,8 @@ npx opendia --tunnel
- Local functionality preserved

Because the tunnel is public, `/sse` requires a bearer token in this mode. The
server prints one at startup; pass `--token=<value>` to pin a fixed one across
restarts. Send it with every request:
server prints one at startup; pass `--token=<value>` (or set `OPENDIA_TOKEN`) to
pin a fixed one across restarts. Send it with every request:

```bash
curl -H "Authorization: Bearer <token>" https://<tunnel>.ngrok-free.app/sse
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ jobs:
run: node test-protocol.js
- name: Connection tests
run: node test-connection.js
- name: Config tests
run: node test-config.js

extension:
name: Browser extension
Expand Down
25 changes: 13 additions & 12 deletions build-dxt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,10 @@ cat > dist/opendia-dxt/manifest.json << 'EOF'
"args": ["${__dirname}/server.js"],
"env": {
"NODE_ENV": "production",
"WS_PORT": "${user_config.ws_port}",
"HTTP_PORT": "${user_config.http_port}",
"ENABLE_TUNNEL": "${user_config.enable_tunnel}",
"SAFETY_MODE": "${user_config.safety_mode}"
"OPENDIA_WS_PORT": "${user_config.ws_port}",
"OPENDIA_HTTP_PORT": "${user_config.http_port}",
"OPENDIA_ENABLE_TUNNEL": "${user_config.enable_tunnel}",
"OPENDIA_TOKEN": "${user_config.auth_token}"
}
}
},
Expand All @@ -173,30 +173,31 @@ cat > dist/opendia-dxt/manifest.json << 'EOF'
"ws_port": {
"type": "number",
"title": "WebSocket Port",
"description": "Port for Chrome/Firefox extension connection",
"description": "Port for Chrome/Firefox extension connection. The extension finds it through the HTTP port.",
"default": 5555,
"min": 1024,
"max": 65535
},
"http_port": {
"type": "number",
"title": "HTTP Port",
"description": "Port for HTTP/SSE server",
"description": "Port for HTTP/SSE server. If you change it to a port the extension does not probe (5556-5558, 3001, 6001-6003), enter the same port in the extension popup under Custom HTTP Port.",
"default": 5556,
"min": 1024,
"max": 65535
},
"enable_tunnel": {
"type": "boolean",
"title": "Auto-Tunnel",
"description": "Automatically create ngrok tunnel for online AI access (requires ngrok)",
"description": "Automatically create ngrok tunnel for online AI access (requires ngrok). Safety Mode is set in the browser extension popup, not here.",
"default": false
},
"safety_mode": {
"type": "boolean",
"title": "Safety Mode",
"description": "Block write/edit tools (element_click, element_fill) by default",
"default": false
"auth_token": {
"type": "string",
"title": "Tunnel Auth Token",
"description": "Bearer token required on /sse in tunnel mode. Leave blank to generate a new one at each start (printed in the server log).",
"sensitive": true,
"required": false
}
},

Expand Down
4 changes: 4 additions & 0 deletions opendia-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ const response = await fetch(`http://localhost:${port}/ports`);
const portInfo = await response.json();
```

A **Custom HTTP Port** set in the popup (saved as `customHttpPort` in
`storage.local`) is checked before this list, for servers started on any other
HTTP port.

### Background Tab Support

All tools support background tab targeting:
Expand Down
30 changes: 28 additions & 2 deletions opendia-extension/src/background/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ browser.storage.local.get(['safetyMode'], (result) => {
safetyModeEnabled = result.safetyMode || false;
});

// HTTP port the user set in the popup, or null. Read fresh on every discovery
// so a change applies without restarting the extension. Promise form, so it
// works on both Chrome MV3 and Firefox's native browser.* API.
async function getCustomHttpPort() {
try {
const result = await browser.storage.local.get('customHttpPort');
const port = Number(result?.customHttpPort);
return Number.isInteger(port) && port >= 1 && port <= 65535 ? port : null;
} catch (error) {
return null;
}
}

// Cross-browser WebSocket connection manager
class ConnectionManager {
constructor() {
Expand Down Expand Up @@ -164,10 +177,15 @@ class ConnectionManager {
}

async discoverServerPorts() {
// Try common HTTP ports to find the server
// Try common HTTP ports to find the server. A custom port from the popup
// goes first, for a server started on a port outside this list.
const commonPorts = [5556, 5557, 5558, 3001, 6001, 6002, 6003];
const customPort = await getCustomHttpPort();
const ports = customPort
? [customPort, ...commonPorts.filter((port) => port !== customPort)]
: commonPorts;

for (const httpPort of commonPorts) {
for (const httpPort of ports) {
try {
const response = await fetch(`http://localhost:${httpPort}/ports`);
if (response.ok) {
Expand Down Expand Up @@ -2013,6 +2031,14 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
safetyModeEnabled = request.enabled;
console.log(`🛡️ Safety Mode ${safetyModeEnabled ? 'ENABLED' : 'DISABLED'}`);
sendResponse({ success: true });
} else if (request.action === "setCustomHttpPort") {
// Force discovery on the next connect so the new port is probed, and try
// now if nothing is connected instead of waiting out the backoff.
MCP_SERVER_URL = 'ws://localhost:5555';
if (!connectionManager.getStatus().connected) {
connectionManager.connect().catch(() => {});
}
sendResponse({ success: true });
}
return true; // Keep the message channel open
});
39 changes: 36 additions & 3 deletions opendia-extension/src/popup/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,8 @@
transform: translateY(0);
}

.safety-mode {
.safety-mode,
.port-setting {
background: rgba(255, 255, 255, 0.6);
padding: 16px;
border-radius: 10px;
Expand All @@ -247,13 +248,15 @@
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

.safety-row {
.safety-row,
.port-row {
display: flex;
justify-content: space-between;
align-items: center;
}

.safety-label {
.safety-label,
.port-label {
color: #374151;
font-weight: 600;
font-size: 0.875rem;
Expand Down Expand Up @@ -310,6 +313,24 @@
.safety-slider:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.port-input {
width: 80px;
padding: 6px 8px;
border: 1px solid rgba(0, 129, 247, 0.2);
border-radius: 6px;
background: rgba(255, 255, 255, 0.5);
color: var(--text-color);
font-size: 0.875rem;
text-align: right;
transition: all 0.3s ease;
}

.port-input:focus {
outline: none;
border-color: rgba(255, 117, 202, 0.4);
box-shadow: 0 2px 8px rgba(255, 117, 202, 0.2);
}
</style>
</head>
<body>
Expand Down Expand Up @@ -365,6 +386,18 @@ <h2>OpenDia</h2>
</div>
</div>

<div class="port-setting">
<div class="port-row">
<span class="port-label tooltip">
Custom HTTP Port
<span class="tooltip-content">
Checked first when finding the server. Set it if the server runs on an HTTP port other than 5556-5558, 3001 or 6001-6003. Leave blank for auto-discovery.
</span>
</span>
<input type="number" id="customHttpPort" class="port-input" min="1" max="65535" step="1" placeholder="Auto">
</div>
</div>

<div class="button-group">
<button id="reconnectBtn">Reconnect</button>
</div>
Expand Down
35 changes: 35 additions & 0 deletions opendia-extension/src/popup/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,41 @@ safetyModeToggle.addEventListener('change', () => {
});
});

// Custom HTTP Port Management
const customHttpPortInput = document.getElementById("customHttpPort");

// Promise form (not a callback) so it behaves the same on Chrome MV3 and
// Firefox's native browser.* API.
function loadCustomHttpPort() {
storageAPI.local.get(['customHttpPort']).then((result) => {
customHttpPortInput.value = result?.customHttpPort ?? '';
});
}

loadCustomHttpPort();

// Save on change; a blank field clears it and goes back to auto-discovery
customHttpPortInput.addEventListener('change', () => {
const raw = customHttpPortInput.value.trim();
const port = Number(raw);
let save;

if (raw === '' && !customHttpPortInput.validity.badInput) {
save = storageAPI.local.remove('customHttpPort');
} else if (Number.isInteger(port) && port >= 1 && port <= 65535) {
save = storageAPI.local.set({ customHttpPort: port });
} else {
loadCustomHttpPort(); // Invalid entry: show the saved value again
return;
}

// Notify background script once the value is stored, so its next port
// discovery reads the new one
save.then(() => {
runtimeAPI.sendMessage({ action: "setCustomHttpPort" });
});
});

// Video speed control based on mouse movement
const logoVideo = document.querySelector('.logo video');
let mouseTimeout;
Expand Down
2 changes: 1 addition & 1 deletion opendia-mcp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"tunnel": "node server.js --tunnel",
"sse-only": "node server.js --sse-only",
"tunnel-sse": "node server.js --tunnel --sse-only",
"test": "node --check server.js && node test-protocol.js && node test-connection.js"
"test": "node --check server.js && node test-protocol.js && node test-connection.js && node test-config.js"
},
"keywords": [
"mcp",
Expand Down
Loading
Loading