openclaw-navigator 5.0.0 → 5.0.2
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/cli.mjs +31 -2
- package/mcp.mjs +58 -3
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -469,7 +469,9 @@ function handleRequest(req, res) {
|
|
|
469
469
|
if (!bridgeState.connected) {
|
|
470
470
|
bridgeState.connected = true;
|
|
471
471
|
bridgeState.connectedAt = Date.now();
|
|
472
|
+
const activeURL = activeTunnelURL ?? `http://localhost:${bridgePort}`;
|
|
472
473
|
console.log(`\n${GREEN}✓${RESET} ${BOLD}Navigator connected!${RESET}`);
|
|
474
|
+
console.log(` ${DIM}Tunnel: ${activeURL}${RESET}`);
|
|
473
475
|
}
|
|
474
476
|
bridgeState.lastHeartbeat = Date.now();
|
|
475
477
|
|
|
@@ -1625,7 +1627,7 @@ module.exports = {
|
|
|
1625
1627
|
|
|
1626
1628
|
mkdirSync(mcporterDir, { recursive: true });
|
|
1627
1629
|
writeFileSync(mcporterConfigPath, JSON.stringify(mcporterConfig, null, 2) + "\n", "utf8");
|
|
1628
|
-
ok("Registered MCP server with mcporter (16 browser
|
|
1630
|
+
ok("Registered MCP server with mcporter (31 tools: 16 browser + 10 AI + 5 profiling)");
|
|
1629
1631
|
info(` Config: ${mcporterConfigPath}`);
|
|
1630
1632
|
|
|
1631
1633
|
// Step 3: Install the navigator-bridge skill so the OC agent knows about all tools
|
|
@@ -1670,7 +1672,9 @@ module.exports = {
|
|
|
1670
1672
|
"mcporter call navigator.navigator_status",
|
|
1671
1673
|
BT,
|
|
1672
1674
|
"",
|
|
1673
|
-
"## All
|
|
1675
|
+
"## All 31 tools",
|
|
1676
|
+
"",
|
|
1677
|
+
"### Browser Control (16 tools)",
|
|
1674
1678
|
"",
|
|
1675
1679
|
"| Tool | What it does |",
|
|
1676
1680
|
"|------|-------------|",
|
|
@@ -1691,6 +1695,31 @@ module.exports = {
|
|
|
1691
1695
|
"| `navigator_query_element` | Inspect DOM element |",
|
|
1692
1696
|
"| `navigator_wait_ready` | Wait for page load |",
|
|
1693
1697
|
"",
|
|
1698
|
+
"### AI Browser Intelligence (10 tools)",
|
|
1699
|
+
"",
|
|
1700
|
+
"| Tool | What it does |",
|
|
1701
|
+
"|------|-------------|",
|
|
1702
|
+
"| `navigator_analyze_page` | Full page analysis — forms, buttons, inputs, links, tables, modals |",
|
|
1703
|
+
"| `navigator_find_element` | Find element by natural language intent (e.g. 'login button') |",
|
|
1704
|
+
"| `navigator_is_ready` | Smart page readiness — checks spinners, skeletons, pending XHR |",
|
|
1705
|
+
"| `navigator_wait_for_element` | Wait for CSS selector to appear (MutationObserver) |",
|
|
1706
|
+
"| `navigator_extract_data` | Extract structured data — text, table, JSON, list, or HTML |",
|
|
1707
|
+
"| `navigator_smart_fill` | Fill form fields by label/name matching (React-safe) |",
|
|
1708
|
+
"| `navigator_intercept_api` | Capture fetch/XHR calls — see what APIs the page uses |",
|
|
1709
|
+
"| `navigator_set_cookies` | Set cookies on the current domain |",
|
|
1710
|
+
"| `navigator_get_performance` | Core Web Vitals and resource transfer stats |",
|
|
1711
|
+
"| `navigator_get_page_state` | Full page state — URL, title, readyState, analysis, timestamp |",
|
|
1712
|
+
"",
|
|
1713
|
+
"### User Profiling (5 tools)",
|
|
1714
|
+
"",
|
|
1715
|
+
"| Tool | What it does |",
|
|
1716
|
+
"|------|-------------|",
|
|
1717
|
+
"| `navigator_get_page_visits` | Get browsing history (auto-recorded page visits) |",
|
|
1718
|
+
"| `navigator_save_page_summary` | Save AI-generated summary of a page |",
|
|
1719
|
+
"| `navigator_get_page_summaries` | Retrieve saved page summaries |",
|
|
1720
|
+
"| `navigator_get_user_profile` | Get aggregated user interest profile |",
|
|
1721
|
+
"| `navigator_save_user_profile` | Save/update user profile from browsing patterns |",
|
|
1722
|
+
"",
|
|
1694
1723
|
"## Usage",
|
|
1695
1724
|
"",
|
|
1696
1725
|
BT + "bash",
|
package/mcp.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* openclaw-navigator MCP server v5.0.
|
|
4
|
+
* openclaw-navigator MCP server v5.0.1
|
|
5
5
|
*
|
|
6
6
|
* Exposes the Navigator bridge HTTP API as MCP tools so the OpenClaw agent
|
|
7
7
|
* can control the browser natively via its tool schema.
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
18
18
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
19
19
|
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
20
|
+
import { readFileSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { homedir } from "node:os";
|
|
20
23
|
|
|
21
24
|
// ── Configuration ─────────────────────────────────────────────────────────
|
|
22
25
|
|
|
@@ -28,25 +31,77 @@ const WAIT_READY_TIMEOUT_MS = 20_000;
|
|
|
28
31
|
// All logging to stderr — stdout is reserved for MCP JSON-RPC protocol
|
|
29
32
|
const log = (...args) => console.error("[navigator-mcp]", ...args);
|
|
30
33
|
|
|
34
|
+
// ── Bridge identity — read persistent token for auth ─────────────────────
|
|
35
|
+
// The bridge stores its identity (pairing code + token) in a file that
|
|
36
|
+
// survives restarts. The MCP server reads this to authenticate requests.
|
|
37
|
+
|
|
38
|
+
const BRIDGE_IDENTITY_PATH = join(homedir(), ".openclaw", "bridge-identity.json");
|
|
39
|
+
|
|
40
|
+
function loadBridgeToken() {
|
|
41
|
+
try {
|
|
42
|
+
const data = JSON.parse(readFileSync(BRIDGE_IDENTITY_PATH, "utf8"));
|
|
43
|
+
if (data.token) {
|
|
44
|
+
log(`Loaded bridge token from ${BRIDGE_IDENTITY_PATH}`);
|
|
45
|
+
return data.token;
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
log(`No bridge identity at ${BRIDGE_IDENTITY_PATH} — running without auth`);
|
|
49
|
+
}
|
|
50
|
+
return process.env.NAVIGATOR_BRIDGE_TOKEN ?? null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let bridgeToken = loadBridgeToken();
|
|
54
|
+
|
|
55
|
+
/** Build common headers — includes auth when a token is available. */
|
|
56
|
+
function authHeaders() {
|
|
57
|
+
const headers = { "Content-Type": "application/json" };
|
|
58
|
+
if (bridgeToken) {
|
|
59
|
+
headers["Authorization"] = `Bearer ${bridgeToken}`;
|
|
60
|
+
}
|
|
61
|
+
return headers;
|
|
62
|
+
}
|
|
63
|
+
|
|
31
64
|
// ── HTTP helpers ──────────────────────────────────────────────────────────
|
|
32
65
|
|
|
33
66
|
async function bridgeGet(path) {
|
|
34
|
-
const res = await fetch(`${BRIDGE_URL}${path}
|
|
67
|
+
const res = await fetch(`${BRIDGE_URL}${path}`, {
|
|
68
|
+
headers: authHeaders(),
|
|
69
|
+
});
|
|
35
70
|
if (!res.ok) {
|
|
71
|
+
// If 401, try reloading the token (bridge may have restarted with new identity)
|
|
72
|
+
if (res.status === 401 && !bridgeGet._retrying) {
|
|
73
|
+
bridgeGet._retrying = true;
|
|
74
|
+
bridgeToken = loadBridgeToken();
|
|
75
|
+
const retry = await bridgeGet(path);
|
|
76
|
+
bridgeGet._retrying = false;
|
|
77
|
+
return retry;
|
|
78
|
+
}
|
|
79
|
+
bridgeGet._retrying = false;
|
|
36
80
|
throw new Error(`Bridge GET ${path} returned ${res.status}`);
|
|
37
81
|
}
|
|
82
|
+
bridgeGet._retrying = false;
|
|
38
83
|
return res.json();
|
|
39
84
|
}
|
|
40
85
|
|
|
41
86
|
async function bridgePost(path, body) {
|
|
42
87
|
const res = await fetch(`${BRIDGE_URL}${path}`, {
|
|
43
88
|
method: "POST",
|
|
44
|
-
headers:
|
|
89
|
+
headers: authHeaders(),
|
|
45
90
|
body: JSON.stringify(body),
|
|
46
91
|
});
|
|
47
92
|
if (!res.ok) {
|
|
93
|
+
// If 401, try reloading the token (bridge may have restarted with new identity)
|
|
94
|
+
if (res.status === 401 && !bridgePost._retrying) {
|
|
95
|
+
bridgePost._retrying = true;
|
|
96
|
+
bridgeToken = loadBridgeToken();
|
|
97
|
+
const retry = await bridgePost(path, body);
|
|
98
|
+
bridgePost._retrying = false;
|
|
99
|
+
return retry;
|
|
100
|
+
}
|
|
101
|
+
bridgePost._retrying = false;
|
|
48
102
|
throw new Error(`Bridge POST ${path} returned ${res.status}`);
|
|
49
103
|
}
|
|
104
|
+
bridgePost._retrying = false;
|
|
50
105
|
return res.json();
|
|
51
106
|
}
|
|
52
107
|
|