livemcp 1.2.1
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 +47 -0
- package/dist/hub.js +171 -0
- package/dist/hub.js.map +7 -0
- package/dist/index.js +519 -0
- package/dist/index.js.map +7 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/DeepakSilaych/chrome-mcp/main/assets/logo.png" width="140" alt="LiveMCP logo" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">livemcp</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">MCP server that gives AI direct access to your real browser — tabs, cookies, session state, and all.</p>
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx livemcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or add to your MCP client config:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"mcpServers": {
|
|
20
|
+
"livemcp": {
|
|
21
|
+
"command": "npx",
|
|
22
|
+
"args": ["-y", "livemcp"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Requirements
|
|
29
|
+
|
|
30
|
+
Install the companion browser extension from the [main repo](https://github.com/DeepakSilaych/chrome-mcp). Click **Connect** in the extension popup after starting the server.
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
| Variable | Default | Description |
|
|
35
|
+
|----------|---------|-------------|
|
|
36
|
+
| `LIVEMCP_PORT` | `17691` | WebSocket port (match in extension popup) |
|
|
37
|
+
|
|
38
|
+
## Architecture
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
MCP Client ←—stdio—→ livemcp ←—WebSocket—→ Browser Extension ←—chrome.*—→ Browser
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Links
|
|
45
|
+
|
|
46
|
+
- [GitHub repo](https://github.com/DeepakSilaych/chrome-mcp) — full source, extension, docs
|
|
47
|
+
- [MIT License](https://github.com/DeepakSilaych/chrome-mcp/blob/main/LICENSE)
|
package/dist/hub.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/hub.ts
|
|
4
|
+
import { createServer as createNetServer } from "node:net";
|
|
5
|
+
import { existsSync, unlinkSync } from "node:fs";
|
|
6
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
7
|
+
|
|
8
|
+
// ../shared/src/protocol.ts
|
|
9
|
+
var DEFAULT_WS_PORT = 17691;
|
|
10
|
+
function isBridgeResponse(v) {
|
|
11
|
+
if (!v || typeof v !== "object") return false;
|
|
12
|
+
const o = v;
|
|
13
|
+
return typeof o.id === "string" && ("result" in o || "error" in o);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/hub.ts
|
|
17
|
+
var HUB_SOCK = process.env.LIVEMCP_HUB_SOCK ?? "/tmp/livemcp-hub.sock";
|
|
18
|
+
var basePort = Number(process.env.LIVEMCP_PORT ?? DEFAULT_WS_PORT) || DEFAULT_WS_PORT;
|
|
19
|
+
var extensionWs = null;
|
|
20
|
+
var pingTimer;
|
|
21
|
+
var sessions = /* @__PURE__ */ new Map();
|
|
22
|
+
var pending = /* @__PURE__ */ new Map();
|
|
23
|
+
function sendToSession(sessionId, msg) {
|
|
24
|
+
const sock = sessions.get(sessionId);
|
|
25
|
+
if (sock && !sock.destroyed) sock.write(JSON.stringify(msg) + "\n");
|
|
26
|
+
}
|
|
27
|
+
function broadcastStatus() {
|
|
28
|
+
const connected = extensionWs !== null && extensionWs.readyState === WebSocket.OPEN;
|
|
29
|
+
for (const sessionId of sessions.keys()) {
|
|
30
|
+
sendToSession(sessionId, { type: "status", connected });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
var activeWss = null;
|
|
34
|
+
function startWss(port) {
|
|
35
|
+
const wss = new WebSocketServer({ host: "127.0.0.1", port });
|
|
36
|
+
wss.once("error", (err) => {
|
|
37
|
+
if (err.code === "EADDRINUSE") {
|
|
38
|
+
wss.close();
|
|
39
|
+
process.stderr.write(`[livemcp-hub] Port ${port} busy, trying ${port + 1}...
|
|
40
|
+
`);
|
|
41
|
+
startWss(port + 1);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
process.stderr.write(`[livemcp-hub] WebSocket error: ${err.message}
|
|
45
|
+
`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
});
|
|
48
|
+
wss.once("listening", () => {
|
|
49
|
+
activeWss = wss;
|
|
50
|
+
if (port !== basePort) {
|
|
51
|
+
process.stderr.write(`
|
|
52
|
+
\u26A0\uFE0F Port ${basePort} was busy \u2014 hub bound to port ${port}.
|
|
53
|
+
`);
|
|
54
|
+
process.stderr.write(` Open the extension popup \u2192 change port to ${port} \u2192 reconnect.
|
|
55
|
+
|
|
56
|
+
`);
|
|
57
|
+
}
|
|
58
|
+
process.stderr.write(`[livemcp-hub] IPC socket : ${HUB_SOCK}
|
|
59
|
+
`);
|
|
60
|
+
process.stderr.write(`[livemcp-hub] WebSocket : ws://127.0.0.1:${port}
|
|
61
|
+
`);
|
|
62
|
+
process.stderr.write("[livemcp-hub] Ready \u2014 waiting for extension and sessions\n");
|
|
63
|
+
});
|
|
64
|
+
wss.on("connection", (ws) => {
|
|
65
|
+
if (extensionWs?.readyState === WebSocket.OPEN) extensionWs.close();
|
|
66
|
+
if (pingTimer) clearInterval(pingTimer);
|
|
67
|
+
extensionWs = ws;
|
|
68
|
+
process.stderr.write("[livemcp-hub] Chrome extension connected\n");
|
|
69
|
+
broadcastStatus();
|
|
70
|
+
pingTimer = setInterval(() => {
|
|
71
|
+
if (ws.readyState === WebSocket.OPEN) ws.ping();
|
|
72
|
+
}, 25e3);
|
|
73
|
+
ws.on("message", (raw) => {
|
|
74
|
+
let parsed;
|
|
75
|
+
try {
|
|
76
|
+
parsed = JSON.parse(raw.toString());
|
|
77
|
+
} catch {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (parsed && typeof parsed === "object" && "keepalive" in parsed) return;
|
|
81
|
+
if (!isBridgeResponse(parsed)) return;
|
|
82
|
+
const res = parsed;
|
|
83
|
+
const sessionId = pending.get(res.id);
|
|
84
|
+
if (!sessionId) return;
|
|
85
|
+
pending.delete(res.id);
|
|
86
|
+
sendToSession(sessionId, { type: "response", id: res.id, result: res.result, error: res.error });
|
|
87
|
+
});
|
|
88
|
+
const onClose = () => {
|
|
89
|
+
if (extensionWs !== ws) return;
|
|
90
|
+
extensionWs = null;
|
|
91
|
+
if (pingTimer) {
|
|
92
|
+
clearInterval(pingTimer);
|
|
93
|
+
pingTimer = void 0;
|
|
94
|
+
}
|
|
95
|
+
process.stderr.write("[livemcp-hub] Chrome extension disconnected\n");
|
|
96
|
+
broadcastStatus();
|
|
97
|
+
};
|
|
98
|
+
ws.on("close", onClose);
|
|
99
|
+
ws.on("error", onClose);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (existsSync(HUB_SOCK)) {
|
|
103
|
+
try {
|
|
104
|
+
unlinkSync(HUB_SOCK);
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
var ipcServer = createNetServer((sock) => {
|
|
109
|
+
let buf = "";
|
|
110
|
+
sock.on("data", (chunk) => {
|
|
111
|
+
buf += chunk.toString();
|
|
112
|
+
const lines = buf.split("\n");
|
|
113
|
+
buf = lines.pop() ?? "";
|
|
114
|
+
for (const line of lines) {
|
|
115
|
+
if (!line.trim()) continue;
|
|
116
|
+
let msg;
|
|
117
|
+
try {
|
|
118
|
+
msg = JSON.parse(line);
|
|
119
|
+
} catch {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (msg.type === "register") {
|
|
123
|
+
sessions.set(msg.sessionId, sock);
|
|
124
|
+
sendToSession(msg.sessionId, { type: "registered", sessionId: msg.sessionId });
|
|
125
|
+
sendToSession(msg.sessionId, { type: "status", connected: extensionWs?.readyState === WebSocket.OPEN });
|
|
126
|
+
process.stderr.write(`[livemcp-hub] Session registered: ${msg.sessionId} (${sessions.size} active)
|
|
127
|
+
`);
|
|
128
|
+
} else if (msg.type === "unregister") {
|
|
129
|
+
sessions.delete(msg.sessionId);
|
|
130
|
+
process.stderr.write(`[livemcp-hub] Session unregistered: ${msg.sessionId} (${sessions.size} active)
|
|
131
|
+
`);
|
|
132
|
+
} else if (msg.type === "request") {
|
|
133
|
+
if (!extensionWs || extensionWs.readyState !== WebSocket.OPEN) {
|
|
134
|
+
sendToSession(msg.sessionId, { type: "response", id: msg.id, error: "Chrome extension not connected to hub" });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
pending.set(msg.id, msg.sessionId);
|
|
138
|
+
const req = { id: msg.id, action: msg.action, params: msg.params };
|
|
139
|
+
extensionWs.send(JSON.stringify(req));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
sock.on("close", () => {
|
|
144
|
+
for (const [sessionId, s] of sessions) {
|
|
145
|
+
if (s === sock) {
|
|
146
|
+
sessions.delete(sessionId);
|
|
147
|
+
process.stderr.write(`[livemcp-hub] Session disconnected: ${sessionId} (${sessions.size} active)
|
|
148
|
+
`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
sock.on("error", () => {
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
ipcServer.listen(HUB_SOCK);
|
|
156
|
+
var shutdown = () => {
|
|
157
|
+
ipcServer.close();
|
|
158
|
+
activeWss?.close();
|
|
159
|
+
try {
|
|
160
|
+
unlinkSync(HUB_SOCK);
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
process.exit(0);
|
|
164
|
+
};
|
|
165
|
+
process.on("SIGTERM", shutdown);
|
|
166
|
+
process.on("SIGINT", shutdown);
|
|
167
|
+
startWss(basePort);
|
|
168
|
+
export {
|
|
169
|
+
HUB_SOCK
|
|
170
|
+
};
|
|
171
|
+
//# sourceMappingURL=hub.js.map
|
package/dist/hub.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/hub.ts", "../../shared/src/protocol.ts"],
|
|
4
|
+
"sourcesContent": ["import { createServer as createNetServer } from \"node:net\";\nimport { existsSync, unlinkSync } from \"node:fs\";\nimport type { Socket } from \"node:net\";\nimport { WebSocket, WebSocketServer } from \"ws\";\nimport type { BridgeRequest, BridgeResponse } from \"@livemcp/shared\";\nimport { isBridgeResponse, DEFAULT_WS_PORT } from \"@livemcp/shared\";\n\nexport const HUB_SOCK = process.env.LIVEMCP_HUB_SOCK ?? \"/tmp/livemcp-hub.sock\";\nconst basePort = Number(process.env.LIVEMCP_PORT ?? DEFAULT_WS_PORT) || DEFAULT_WS_PORT;\n\n// session \u2192 hub (Unix socket, newline-delimited JSON)\ntype IpcRegister = { type: \"register\"; sessionId: string };\ntype IpcUnregister = { type: \"unregister\"; sessionId: string };\ntype IpcRequest = { type: \"request\"; sessionId: string; id: string; action: string; params?: Record<string, unknown> };\ntype IpcMessage = IpcRegister | IpcUnregister | IpcRequest;\n\n// hub \u2192 session\ntype IpcRegistered = { type: \"registered\"; sessionId: string };\ntype IpcStatus = { type: \"status\"; connected: boolean };\ntype IpcResponse = { type: \"response\"; id: string; result?: unknown; error?: string };\n\nlet extensionWs: WebSocket | null = null;\nlet pingTimer: ReturnType<typeof setInterval> | undefined;\nconst sessions = new Map<string, Socket>(); // sessionId \u2192 IPC socket\nconst pending = new Map<string, string>(); // requestId \u2192 sessionId\n\nfunction sendToSession(sessionId: string, msg: IpcRegistered | IpcStatus | IpcResponse): void {\n const sock = sessions.get(sessionId);\n if (sock && !sock.destroyed) sock.write(JSON.stringify(msg) + \"\\n\");\n}\n\nfunction broadcastStatus(): void {\n const connected = extensionWs !== null && extensionWs.readyState === WebSocket.OPEN;\n for (const sessionId of sessions.keys()) {\n sendToSession(sessionId, { type: \"status\", connected });\n }\n}\n\n// \u2500\u2500 Layer 1: WebSocket server for Chrome extension \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nlet activeWss: WebSocketServer | null = null;\n\nfunction startWss(port: number): void {\n const wss = new WebSocketServer({ host: \"127.0.0.1\", port });\n\n wss.once(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n wss.close();\n process.stderr.write(`[livemcp-hub] Port ${port} busy, trying ${port + 1}...\\n`);\n startWss(port + 1);\n return;\n }\n process.stderr.write(`[livemcp-hub] WebSocket error: ${err.message}\\n`);\n process.exit(1);\n });\n\n wss.once(\"listening\", () => {\n activeWss = wss;\n if (port !== basePort) {\n process.stderr.write(`\\n\u26A0\uFE0F Port ${basePort} was busy \u2014 hub bound to port ${port}.\\n`);\n process.stderr.write(` Open the extension popup \u2192 change port to ${port} \u2192 reconnect.\\n\\n`);\n }\n process.stderr.write(`[livemcp-hub] IPC socket : ${HUB_SOCK}\\n`);\n process.stderr.write(`[livemcp-hub] WebSocket : ws://127.0.0.1:${port}\\n`);\n process.stderr.write(\"[livemcp-hub] Ready \u2014 waiting for extension and sessions\\n\");\n });\n\n wss.on(\"connection\", (ws) => {\n if (extensionWs?.readyState === WebSocket.OPEN) extensionWs.close();\n if (pingTimer) clearInterval(pingTimer);\n\n extensionWs = ws;\n process.stderr.write(\"[livemcp-hub] Chrome extension connected\\n\");\n broadcastStatus();\n\n pingTimer = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) ws.ping();\n }, 25_000);\n\n ws.on(\"message\", (raw) => {\n let parsed: unknown;\n try { parsed = JSON.parse(raw.toString()); } catch { return; }\n if (parsed && typeof parsed === \"object\" && \"keepalive\" in (parsed as Record<string, unknown>)) return;\n if (!isBridgeResponse(parsed)) return;\n\n const res = parsed as BridgeResponse;\n const sessionId = pending.get(res.id);\n if (!sessionId) return;\n pending.delete(res.id);\n sendToSession(sessionId, { type: \"response\", id: res.id, result: res.result, error: res.error });\n });\n\n const onClose = () => {\n if (extensionWs !== ws) return;\n extensionWs = null;\n if (pingTimer) { clearInterval(pingTimer); pingTimer = undefined; }\n process.stderr.write(\"[livemcp-hub] Chrome extension disconnected\\n\");\n broadcastStatus();\n };\n ws.on(\"close\", onClose);\n ws.on(\"error\", onClose);\n });\n}\n\n// \u2500\u2500 Layer 3: Unix socket IPC server for MCP sessions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nif (existsSync(HUB_SOCK)) {\n try { unlinkSync(HUB_SOCK); } catch {}\n}\n\nconst ipcServer = createNetServer((sock) => {\n let buf = \"\";\n\n sock.on(\"data\", (chunk) => {\n buf += chunk.toString();\n const lines = buf.split(\"\\n\");\n buf = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.trim()) continue;\n let msg: IpcMessage;\n try { msg = JSON.parse(line) as IpcMessage; } catch { continue; }\n\n if (msg.type === \"register\") {\n sessions.set(msg.sessionId, sock);\n sendToSession(msg.sessionId, { type: \"registered\", sessionId: msg.sessionId });\n sendToSession(msg.sessionId, { type: \"status\", connected: extensionWs?.readyState === WebSocket.OPEN });\n process.stderr.write(`[livemcp-hub] Session registered: ${msg.sessionId} (${sessions.size} active)\\n`);\n\n } else if (msg.type === \"unregister\") {\n sessions.delete(msg.sessionId);\n process.stderr.write(`[livemcp-hub] Session unregistered: ${msg.sessionId} (${sessions.size} active)\\n`);\n\n } else if (msg.type === \"request\") {\n if (!extensionWs || extensionWs.readyState !== WebSocket.OPEN) {\n sendToSession(msg.sessionId, { type: \"response\", id: msg.id, error: \"Chrome extension not connected to hub\" });\n return;\n }\n pending.set(msg.id, msg.sessionId);\n const req: BridgeRequest = { id: msg.id, action: msg.action as BridgeRequest[\"action\"], params: msg.params };\n extensionWs.send(JSON.stringify(req));\n }\n }\n });\n\n sock.on(\"close\", () => {\n for (const [sessionId, s] of sessions) {\n if (s === sock) {\n sessions.delete(sessionId);\n process.stderr.write(`[livemcp-hub] Session disconnected: ${sessionId} (${sessions.size} active)\\n`);\n }\n }\n });\n\n sock.on(\"error\", () => {});\n});\n\nipcServer.listen(HUB_SOCK);\n\nconst shutdown = () => {\n ipcServer.close();\n activeWss?.close();\n try { unlinkSync(HUB_SOCK); } catch {}\n process.exit(0);\n};\nprocess.on(\"SIGTERM\", shutdown);\nprocess.on(\"SIGINT\", shutdown);\n\nstartWss(basePort);\n", "export const DEFAULT_WS_PORT = 17691;\n\nexport const BRIDGE_ACTIONS = [\n \"tabs.list\",\n \"tabs.getActive\",\n \"tabs.switch\",\n \"tabs.close\",\n \"tabs.create\",\n \"content.getPage\",\n \"content.getSelection\",\n \"screenshot.capture\",\n \"navigate.to\",\n \"navigate.back\",\n \"navigate.forward\",\n \"navigate.reload\",\n \"navigate.andWait\",\n \"network.startCapture\",\n \"network.stopCapture\",\n \"network.getCaptured\",\n \"console.startCapture\",\n \"console.stopCapture\",\n \"console.getLogs\",\n \"interact.click\",\n \"interact.type\",\n \"interact.fillForm\",\n \"interact.clickAndWait\",\n \"interact.scroll\",\n \"cookies.get\",\n \"cookies.getLocalStorage\",\n \"page.snapshot\",\n] as const;\n\nexport type BridgeAction = (typeof BRIDGE_ACTIONS)[number];\n\nexport type BridgeRequest = {\n id: string;\n action: BridgeAction;\n params?: Record<string, unknown>;\n};\n\nexport type BridgeResponse = {\n id: string;\n result?: unknown;\n error?: string;\n};\n\nexport function isBridgeRequest(v: unknown): v is BridgeRequest {\n if (!v || typeof v !== \"object\") return false;\n const o = v as Record<string, unknown>;\n const p = o.params;\n const paramsOk =\n p === undefined || (typeof p === \"object\" && p !== null && !Array.isArray(p));\n return typeof o.id === \"string\" && typeof o.action === \"string\" && paramsOk;\n}\n\nexport function isBridgeResponse(v: unknown): v is BridgeResponse {\n if (!v || typeof v !== \"object\") return false;\n const o = v as Record<string, unknown>;\n return typeof o.id === \"string\" && (\"result\" in o || \"error\" in o);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
|
|
7
|
+
// src/hub-client.ts
|
|
8
|
+
import { createConnection } from "node:net";
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
var HUB_SOCK = process.env.LIVEMCP_HUB_SOCK ?? "/tmp/livemcp-hub.sock";
|
|
11
|
+
function createHubClient(requestTimeoutMs = 3e4) {
|
|
12
|
+
const sessionId = randomUUID();
|
|
13
|
+
const pending = /* @__PURE__ */ new Map();
|
|
14
|
+
let hubConnected = false;
|
|
15
|
+
let extensionConnected = false;
|
|
16
|
+
let buf = "";
|
|
17
|
+
const sock = createConnection(HUB_SOCK);
|
|
18
|
+
const send = (msg) => {
|
|
19
|
+
sock.write(JSON.stringify(msg) + "\n");
|
|
20
|
+
};
|
|
21
|
+
sock.on("connect", () => {
|
|
22
|
+
hubConnected = true;
|
|
23
|
+
send({ type: "register", sessionId });
|
|
24
|
+
});
|
|
25
|
+
sock.on("data", (chunk) => {
|
|
26
|
+
buf += chunk.toString();
|
|
27
|
+
const lines = buf.split("\n");
|
|
28
|
+
buf = lines.pop() ?? "";
|
|
29
|
+
for (const line of lines) {
|
|
30
|
+
if (!line.trim()) continue;
|
|
31
|
+
let msg;
|
|
32
|
+
try {
|
|
33
|
+
msg = JSON.parse(line);
|
|
34
|
+
} catch {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (msg.type === "status") {
|
|
38
|
+
extensionConnected = Boolean(msg.connected);
|
|
39
|
+
} else if (msg.type === "response") {
|
|
40
|
+
const id = msg.id;
|
|
41
|
+
const slot = pending.get(id);
|
|
42
|
+
if (!slot) continue;
|
|
43
|
+
clearTimeout(slot.timer);
|
|
44
|
+
pending.delete(id);
|
|
45
|
+
if (msg.error !== void 0) {
|
|
46
|
+
slot.reject(new Error(msg.error));
|
|
47
|
+
} else {
|
|
48
|
+
slot.resolve(msg.result);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
sock.on("close", () => {
|
|
54
|
+
hubConnected = false;
|
|
55
|
+
extensionConnected = false;
|
|
56
|
+
for (const [, slot] of pending) {
|
|
57
|
+
clearTimeout(slot.timer);
|
|
58
|
+
slot.reject(new Error("Hub disconnected"));
|
|
59
|
+
}
|
|
60
|
+
pending.clear();
|
|
61
|
+
});
|
|
62
|
+
sock.on("error", (err) => {
|
|
63
|
+
process.stderr.write(`[livemcp] Hub connection error: ${err.message}
|
|
64
|
+
`);
|
|
65
|
+
if (err.code === "ENOENT") {
|
|
66
|
+
process.stderr.write(`[livemcp] Hub not running. Start it with: livemcp-hub
|
|
67
|
+
`);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const isConnected = () => hubConnected && extensionConnected;
|
|
71
|
+
const request = (action, params = {}) => {
|
|
72
|
+
if (!hubConnected) {
|
|
73
|
+
return Promise.reject(new Error("livemcp-hub is not running. Start it with: livemcp-hub"));
|
|
74
|
+
}
|
|
75
|
+
if (!extensionConnected) {
|
|
76
|
+
return Promise.reject(new Error("Chrome extension not connected to hub"));
|
|
77
|
+
}
|
|
78
|
+
const id = randomUUID();
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const timer = setTimeout(() => {
|
|
81
|
+
pending.delete(id);
|
|
82
|
+
reject(new Error("Bridge request timed out"));
|
|
83
|
+
}, requestTimeoutMs);
|
|
84
|
+
pending.set(id, { resolve, reject, timer });
|
|
85
|
+
send({ type: "request", sessionId, id, action, params });
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
const close = async () => {
|
|
89
|
+
if (hubConnected) send({ type: "unregister", sessionId });
|
|
90
|
+
for (const [, slot] of pending) {
|
|
91
|
+
clearTimeout(slot.timer);
|
|
92
|
+
slot.reject(new Error("Bridge closed"));
|
|
93
|
+
}
|
|
94
|
+
pending.clear();
|
|
95
|
+
sock.destroy();
|
|
96
|
+
};
|
|
97
|
+
return { isConnected, request, close };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/tools/console.ts
|
|
101
|
+
import { z as z2 } from "zod";
|
|
102
|
+
|
|
103
|
+
// src/tools/helpers.ts
|
|
104
|
+
import { z } from "zod";
|
|
105
|
+
|
|
106
|
+
// src/toolResult.ts
|
|
107
|
+
function okJson(data) {
|
|
108
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
109
|
+
}
|
|
110
|
+
function errText(message) {
|
|
111
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/tools/helpers.ts
|
|
115
|
+
var tabSpecSchema = {
|
|
116
|
+
tabId: z.number().int().positive().optional().describe("Numeric tab ID (prefer tabUrl/tabTitle \u2014 numeric IDs change when tabs are opened)"),
|
|
117
|
+
tabUrl: z.string().optional().describe("Target the first tab whose URL contains this substring (e.g. 'github.com')"),
|
|
118
|
+
tabTitle: z.string().optional().describe("Target the first tab whose title contains this substring (e.g. 'Dashboard')")
|
|
119
|
+
};
|
|
120
|
+
async function bridgeCall(bridge2, action, params = {}) {
|
|
121
|
+
try {
|
|
122
|
+
const r = await bridge2.request(action, params);
|
|
123
|
+
return okJson(r);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
return errText(e instanceof Error ? e.message : String(e));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/tools/console.ts
|
|
130
|
+
function registerConsoleTools(mcp2, bridge2) {
|
|
131
|
+
mcp2.registerTool(
|
|
132
|
+
"start_console_capture",
|
|
133
|
+
{
|
|
134
|
+
description: "Attach debugger and start recording console log entries for a tab",
|
|
135
|
+
inputSchema: { tabId: z2.number().int().positive() }
|
|
136
|
+
},
|
|
137
|
+
async ({ tabId }) => bridgeCall(bridge2, "console.startCapture", { tabId })
|
|
138
|
+
);
|
|
139
|
+
mcp2.registerTool(
|
|
140
|
+
"stop_console_capture",
|
|
141
|
+
{
|
|
142
|
+
description: "Stop console recording for the tab",
|
|
143
|
+
inputSchema: { tabId: z2.number().int().positive() }
|
|
144
|
+
},
|
|
145
|
+
async ({ tabId }) => bridgeCall(bridge2, "console.stopCapture", { tabId })
|
|
146
|
+
);
|
|
147
|
+
mcp2.registerTool(
|
|
148
|
+
"get_console_logs",
|
|
149
|
+
{
|
|
150
|
+
description: "Return captured console entries for a tab",
|
|
151
|
+
inputSchema: {
|
|
152
|
+
tabId: z2.number().int().positive(),
|
|
153
|
+
clearAfter: z2.boolean().optional()
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
async (args) => bridgeCall(bridge2, "console.getLogs", {
|
|
157
|
+
tabId: args.tabId,
|
|
158
|
+
clearAfter: args.clearAfter ?? false
|
|
159
|
+
})
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/tools/content.ts
|
|
164
|
+
import { z as z3 } from "zod";
|
|
165
|
+
var formatSchema = z3.enum(["text", "html", "markdown"]);
|
|
166
|
+
function registerContentTools(mcp2, bridge2) {
|
|
167
|
+
mcp2.registerTool(
|
|
168
|
+
"get_page_content",
|
|
169
|
+
{
|
|
170
|
+
description: "Read page content from a tab as text, html, or markdown-oriented plain text",
|
|
171
|
+
inputSchema: {
|
|
172
|
+
...tabSpecSchema,
|
|
173
|
+
format: formatSchema.optional()
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
async (args) => bridgeCall(bridge2, "content.getPage", {
|
|
177
|
+
tabId: args.tabId,
|
|
178
|
+
tabUrl: args.tabUrl,
|
|
179
|
+
tabTitle: args.tabTitle,
|
|
180
|
+
format: args.format ?? "text"
|
|
181
|
+
})
|
|
182
|
+
);
|
|
183
|
+
mcp2.registerTool(
|
|
184
|
+
"get_selected_text",
|
|
185
|
+
{
|
|
186
|
+
description: "Get the current text selection in a tab",
|
|
187
|
+
inputSchema: { ...tabSpecSchema }
|
|
188
|
+
},
|
|
189
|
+
async (args) => bridgeCall(bridge2, "content.getSelection", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle })
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/tools/cookies.ts
|
|
194
|
+
import { z as z4 } from "zod";
|
|
195
|
+
function registerCookieTools(mcp2, bridge2) {
|
|
196
|
+
mcp2.registerTool(
|
|
197
|
+
"get_cookies",
|
|
198
|
+
{
|
|
199
|
+
description: "List cookies for a URL",
|
|
200
|
+
inputSchema: { url: z4.string() }
|
|
201
|
+
},
|
|
202
|
+
async ({ url }) => bridgeCall(bridge2, "cookies.get", { url })
|
|
203
|
+
);
|
|
204
|
+
mcp2.registerTool(
|
|
205
|
+
"get_local_storage",
|
|
206
|
+
{
|
|
207
|
+
description: "Read localStorage for a tab origin",
|
|
208
|
+
inputSchema: { ...tabSpecSchema }
|
|
209
|
+
},
|
|
210
|
+
async (args) => bridgeCall(bridge2, "cookies.getLocalStorage", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle })
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/tools/interact.ts
|
|
215
|
+
import { z as z5 } from "zod";
|
|
216
|
+
var fieldSchema = z5.object({
|
|
217
|
+
selector: z5.string(),
|
|
218
|
+
value: z5.string()
|
|
219
|
+
});
|
|
220
|
+
function registerInteractTools(mcp2, bridge2) {
|
|
221
|
+
mcp2.registerTool(
|
|
222
|
+
"click_element",
|
|
223
|
+
{
|
|
224
|
+
description: "Click the first element matching a CSS selector",
|
|
225
|
+
inputSchema: {
|
|
226
|
+
selector: z5.string(),
|
|
227
|
+
...tabSpecSchema
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
async (args) => bridgeCall(bridge2, "interact.click", { selector: args.selector, tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle })
|
|
231
|
+
);
|
|
232
|
+
mcp2.registerTool(
|
|
233
|
+
"type_text",
|
|
234
|
+
{
|
|
235
|
+
description: "Type into an input element matched by selector",
|
|
236
|
+
inputSchema: {
|
|
237
|
+
selector: z5.string(),
|
|
238
|
+
text: z5.string(),
|
|
239
|
+
clear: z5.boolean().optional(),
|
|
240
|
+
...tabSpecSchema
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
async (args) => bridgeCall(bridge2, "interact.type", {
|
|
244
|
+
selector: args.selector,
|
|
245
|
+
text: args.text,
|
|
246
|
+
clear: args.clear ?? false,
|
|
247
|
+
tabId: args.tabId,
|
|
248
|
+
tabUrl: args.tabUrl,
|
|
249
|
+
tabTitle: args.tabTitle
|
|
250
|
+
})
|
|
251
|
+
);
|
|
252
|
+
mcp2.registerTool(
|
|
253
|
+
"fill_form",
|
|
254
|
+
{
|
|
255
|
+
description: "Fill multiple form fields by selector. Supports input, textarea, select (dropdown), checkbox (value=true/false), radio, and contentEditable. Set submit=true to submit the form after filling.",
|
|
256
|
+
inputSchema: {
|
|
257
|
+
fields: z5.array(fieldSchema),
|
|
258
|
+
submit: z5.boolean().optional().describe("Submit the form after filling all fields"),
|
|
259
|
+
...tabSpecSchema
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
async (args) => bridgeCall(bridge2, "interact.fillForm", {
|
|
263
|
+
fields: args.fields,
|
|
264
|
+
submit: args.submit ?? false,
|
|
265
|
+
tabId: args.tabId,
|
|
266
|
+
tabUrl: args.tabUrl,
|
|
267
|
+
tabTitle: args.tabTitle
|
|
268
|
+
})
|
|
269
|
+
);
|
|
270
|
+
mcp2.registerTool(
|
|
271
|
+
"click_and_wait",
|
|
272
|
+
{
|
|
273
|
+
description: "Click an element and wait for a result. Use waitForNavigation=true when the click triggers a page navigation. Use waitFor=selector to wait for a specific element to appear (e.g. a modal or result). Saves an extra round-trip vs click_element + polling.",
|
|
274
|
+
inputSchema: {
|
|
275
|
+
selector: z5.string(),
|
|
276
|
+
waitForNavigation: z5.boolean().optional().describe("Wait for page navigation to complete"),
|
|
277
|
+
waitFor: z5.string().optional().describe("CSS selector to wait for after clicking"),
|
|
278
|
+
timeout: z5.number().int().positive().optional().describe("Timeout in ms (default 5000)"),
|
|
279
|
+
...tabSpecSchema
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
async (args) => bridgeCall(bridge2, "interact.clickAndWait", {
|
|
283
|
+
selector: args.selector,
|
|
284
|
+
waitForNavigation: args.waitForNavigation ?? false,
|
|
285
|
+
waitFor: args.waitFor,
|
|
286
|
+
timeout: args.timeout,
|
|
287
|
+
tabId: args.tabId,
|
|
288
|
+
tabUrl: args.tabUrl,
|
|
289
|
+
tabTitle: args.tabTitle
|
|
290
|
+
})
|
|
291
|
+
);
|
|
292
|
+
mcp2.registerTool(
|
|
293
|
+
"scroll_page",
|
|
294
|
+
{
|
|
295
|
+
description: "Scroll the page by direction",
|
|
296
|
+
inputSchema: {
|
|
297
|
+
direction: z5.enum(["up", "down", "left", "right"]),
|
|
298
|
+
amount: z5.number().int().positive().optional(),
|
|
299
|
+
...tabSpecSchema
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
async (args) => bridgeCall(bridge2, "interact.scroll", {
|
|
303
|
+
direction: args.direction,
|
|
304
|
+
amount: args.amount ?? 400,
|
|
305
|
+
tabId: args.tabId,
|
|
306
|
+
tabUrl: args.tabUrl,
|
|
307
|
+
tabTitle: args.tabTitle
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// src/tools/navigate.ts
|
|
313
|
+
import { z as z6 } from "zod";
|
|
314
|
+
function registerNavigateTools(mcp2, bridge2) {
|
|
315
|
+
mcp2.registerTool(
|
|
316
|
+
"navigate_to",
|
|
317
|
+
{
|
|
318
|
+
description: "Navigate a tab to a URL",
|
|
319
|
+
inputSchema: {
|
|
320
|
+
url: z6.string(),
|
|
321
|
+
...tabSpecSchema
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
async (args) => bridgeCall(bridge2, "navigate.to", { url: args.url, tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle })
|
|
325
|
+
);
|
|
326
|
+
mcp2.registerTool(
|
|
327
|
+
"go_back",
|
|
328
|
+
{
|
|
329
|
+
description: "History back for a tab",
|
|
330
|
+
inputSchema: { ...tabSpecSchema }
|
|
331
|
+
},
|
|
332
|
+
async (args) => bridgeCall(bridge2, "navigate.back", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle })
|
|
333
|
+
);
|
|
334
|
+
mcp2.registerTool(
|
|
335
|
+
"go_forward",
|
|
336
|
+
{
|
|
337
|
+
description: "History forward for a tab",
|
|
338
|
+
inputSchema: { ...tabSpecSchema }
|
|
339
|
+
},
|
|
340
|
+
async (args) => bridgeCall(bridge2, "navigate.forward", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle })
|
|
341
|
+
);
|
|
342
|
+
mcp2.registerTool(
|
|
343
|
+
"reload_tab",
|
|
344
|
+
{
|
|
345
|
+
description: "Reload a tab",
|
|
346
|
+
inputSchema: { ...tabSpecSchema }
|
|
347
|
+
},
|
|
348
|
+
async (args) => bridgeCall(bridge2, "navigate.reload", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle })
|
|
349
|
+
);
|
|
350
|
+
mcp2.registerTool(
|
|
351
|
+
"navigate_and_wait",
|
|
352
|
+
{
|
|
353
|
+
description: "Navigate to a URL and wait for the page to fully load. Optionally wait for a specific CSS selector to appear after load. Faster than navigate_to + polling for content.",
|
|
354
|
+
inputSchema: {
|
|
355
|
+
url: z6.string(),
|
|
356
|
+
waitFor: z6.string().optional().describe("CSS selector to wait for after page load"),
|
|
357
|
+
timeout: z6.number().int().positive().optional().describe("Timeout in ms (default 10000)"),
|
|
358
|
+
...tabSpecSchema
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
async (args) => bridgeCall(bridge2, "navigate.andWait", {
|
|
362
|
+
url: args.url,
|
|
363
|
+
waitFor: args.waitFor,
|
|
364
|
+
timeout: args.timeout,
|
|
365
|
+
tabId: args.tabId,
|
|
366
|
+
tabUrl: args.tabUrl,
|
|
367
|
+
tabTitle: args.tabTitle
|
|
368
|
+
})
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/tools/network.ts
|
|
373
|
+
import { z as z7 } from "zod";
|
|
374
|
+
function registerNetworkTools(mcp2, bridge2) {
|
|
375
|
+
mcp2.registerTool(
|
|
376
|
+
"start_network_capture",
|
|
377
|
+
{
|
|
378
|
+
description: "Attach debugger and start recording network requests for a tab",
|
|
379
|
+
inputSchema: { tabId: z7.number().int().positive() }
|
|
380
|
+
},
|
|
381
|
+
async ({ tabId }) => bridgeCall(bridge2, "network.startCapture", { tabId })
|
|
382
|
+
);
|
|
383
|
+
mcp2.registerTool(
|
|
384
|
+
"stop_network_capture",
|
|
385
|
+
{
|
|
386
|
+
description: "Stop network recording and detach debugger for that capture on the tab",
|
|
387
|
+
inputSchema: { tabId: z7.number().int().positive() }
|
|
388
|
+
},
|
|
389
|
+
async ({ tabId }) => bridgeCall(bridge2, "network.stopCapture", { tabId })
|
|
390
|
+
);
|
|
391
|
+
mcp2.registerTool(
|
|
392
|
+
"get_captured_requests",
|
|
393
|
+
{
|
|
394
|
+
description: "Return captured HTTP requests for a tab",
|
|
395
|
+
inputSchema: {
|
|
396
|
+
tabId: z7.number().int().positive(),
|
|
397
|
+
clearAfter: z7.boolean().optional()
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
async (args) => bridgeCall(bridge2, "network.getCaptured", {
|
|
401
|
+
tabId: args.tabId,
|
|
402
|
+
clearAfter: args.clearAfter ?? false
|
|
403
|
+
})
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/tools/screenshot.ts
|
|
408
|
+
function registerScreenshotTools(mcp2, bridge2) {
|
|
409
|
+
mcp2.registerTool(
|
|
410
|
+
"take_screenshot",
|
|
411
|
+
{
|
|
412
|
+
description: "Capture the visible area of a tab as PNG base64 data URL",
|
|
413
|
+
inputSchema: { ...tabSpecSchema }
|
|
414
|
+
},
|
|
415
|
+
async (args) => bridgeCall(bridge2, "screenshot.capture", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle })
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/tools/snapshot.ts
|
|
420
|
+
function registerSnapshotTools(mcp2, bridge2) {
|
|
421
|
+
mcp2.registerTool(
|
|
422
|
+
"get_page_snapshot",
|
|
423
|
+
{
|
|
424
|
+
description: "Get a full page snapshot in one call: visual screenshot + interactive elements (inputs, buttons, links with selectors) + headings + URL. Use this instead of separate screenshot + get_page_content calls to save round-trips.",
|
|
425
|
+
inputSchema: { ...tabSpecSchema }
|
|
426
|
+
},
|
|
427
|
+
async (args) => {
|
|
428
|
+
try {
|
|
429
|
+
const result = await bridge2.request("page.snapshot", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle });
|
|
430
|
+
const base64 = result.screenshot.replace(/^data:image\/[^;]+;base64,/, "");
|
|
431
|
+
return {
|
|
432
|
+
content: [
|
|
433
|
+
{ type: "image", data: base64, mimeType: "image/png" },
|
|
434
|
+
{
|
|
435
|
+
type: "text",
|
|
436
|
+
text: JSON.stringify(
|
|
437
|
+
{
|
|
438
|
+
url: result.url,
|
|
439
|
+
title: result.title,
|
|
440
|
+
headings: result.headings,
|
|
441
|
+
interactive: result.interactive
|
|
442
|
+
},
|
|
443
|
+
null,
|
|
444
|
+
2
|
|
445
|
+
)
|
|
446
|
+
}
|
|
447
|
+
]
|
|
448
|
+
};
|
|
449
|
+
} catch (e) {
|
|
450
|
+
return errText(e instanceof Error ? e.message : String(e));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// src/tools/tabs.ts
|
|
457
|
+
import { z as z8 } from "zod";
|
|
458
|
+
function registerTabTools(mcp2, bridge2) {
|
|
459
|
+
mcp2.registerTool(
|
|
460
|
+
"list_tabs",
|
|
461
|
+
{ description: "List open tabs in the user Chrome profile", inputSchema: z8.object({}) },
|
|
462
|
+
async () => bridgeCall(bridge2, "tabs.list", {})
|
|
463
|
+
);
|
|
464
|
+
mcp2.registerTool(
|
|
465
|
+
"get_active_tab",
|
|
466
|
+
{ description: "Get the active tab in the current window", inputSchema: z8.object({}) },
|
|
467
|
+
async () => bridgeCall(bridge2, "tabs.getActive", {})
|
|
468
|
+
);
|
|
469
|
+
mcp2.registerTool(
|
|
470
|
+
"switch_tab",
|
|
471
|
+
{
|
|
472
|
+
description: "Focus a tab by id",
|
|
473
|
+
inputSchema: { tabId: z8.number().int().positive() }
|
|
474
|
+
},
|
|
475
|
+
async ({ tabId }) => bridgeCall(bridge2, "tabs.switch", { tabId })
|
|
476
|
+
);
|
|
477
|
+
mcp2.registerTool(
|
|
478
|
+
"close_tab",
|
|
479
|
+
{
|
|
480
|
+
description: "Close a tab by id",
|
|
481
|
+
inputSchema: { tabId: z8.number().int().positive() }
|
|
482
|
+
},
|
|
483
|
+
async ({ tabId }) => bridgeCall(bridge2, "tabs.close", { tabId })
|
|
484
|
+
);
|
|
485
|
+
mcp2.registerTool(
|
|
486
|
+
"create_tab",
|
|
487
|
+
{
|
|
488
|
+
description: "Open a new tab with optional URL",
|
|
489
|
+
inputSchema: { url: z8.string().min(1).optional() }
|
|
490
|
+
},
|
|
491
|
+
async ({ url }) => bridgeCall(bridge2, "tabs.create", url ? { url } : {})
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// src/tools/index.ts
|
|
496
|
+
function registerAllTools(mcp2, bridge2) {
|
|
497
|
+
registerTabTools(mcp2, bridge2);
|
|
498
|
+
registerContentTools(mcp2, bridge2);
|
|
499
|
+
registerScreenshotTools(mcp2, bridge2);
|
|
500
|
+
registerSnapshotTools(mcp2, bridge2);
|
|
501
|
+
registerNavigateTools(mcp2, bridge2);
|
|
502
|
+
registerNetworkTools(mcp2, bridge2);
|
|
503
|
+
registerConsoleTools(mcp2, bridge2);
|
|
504
|
+
registerInteractTools(mcp2, bridge2);
|
|
505
|
+
registerCookieTools(mcp2, bridge2);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/index.ts
|
|
509
|
+
var bridge = createHubClient();
|
|
510
|
+
var mcp = new McpServer(
|
|
511
|
+
{ name: "livemcp", version: "1.2.1" },
|
|
512
|
+
{
|
|
513
|
+
instructions: "Controls the user's real browser tabs via the LiveMCP bridge extension. Tools fail until the extension connects to the WebSocket on localhost. Prefer reading existing tabs over opening new ones."
|
|
514
|
+
}
|
|
515
|
+
);
|
|
516
|
+
registerAllTools(mcp, bridge);
|
|
517
|
+
var transport = new StdioServerTransport();
|
|
518
|
+
await mcp.connect(transport);
|
|
519
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/hub-client.ts", "../src/tools/console.ts", "../src/tools/helpers.ts", "../src/toolResult.ts", "../src/tools/content.ts", "../src/tools/cookies.ts", "../src/tools/interact.ts", "../src/tools/navigate.ts", "../src/tools/network.ts", "../src/tools/screenshot.ts", "../src/tools/snapshot.ts", "../src/tools/tabs.ts", "../src/tools/index.ts"],
|
|
4
|
+
"sourcesContent": ["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createHubClient } from \"./hub-client.js\";\nimport { registerAllTools } from \"./tools/index.js\";\n\nconst bridge = createHubClient();\n\nconst mcp = new McpServer(\n { name: \"livemcp\", version: \"1.2.1\" },\n {\n instructions:\n \"Controls the user's real browser tabs via the LiveMCP bridge extension. Tools fail until the extension connects to the WebSocket on localhost. Prefer reading existing tabs over opening new ones.\",\n },\n);\n\nregisterAllTools(mcp, bridge);\n\nconst transport = new StdioServerTransport();\nawait mcp.connect(transport);\n", "import { createConnection } from \"node:net\";\nimport { randomUUID } from \"node:crypto\";\nimport type { BridgeAction } from \"@livemcp/shared\";\nimport type { Bridge } from \"./bridge.js\";\n\nexport const HUB_SOCK = process.env.LIVEMCP_HUB_SOCK ?? \"/tmp/livemcp-hub.sock\";\n\ntype Pending = {\n resolve: (value: unknown) => void;\n reject: (reason: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\nexport function createHubClient(requestTimeoutMs = 30_000): Bridge {\n const sessionId = randomUUID();\n const pending = new Map<string, Pending>();\n let hubConnected = false;\n let extensionConnected = false;\n let buf = \"\";\n\n const sock = createConnection(HUB_SOCK);\n\n const send = (msg: object) => {\n sock.write(JSON.stringify(msg) + \"\\n\");\n };\n\n sock.on(\"connect\", () => {\n hubConnected = true;\n send({ type: \"register\", sessionId });\n });\n\n sock.on(\"data\", (chunk) => {\n buf += chunk.toString();\n const lines = buf.split(\"\\n\");\n buf = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.trim()) continue;\n let msg: Record<string, unknown>;\n try { msg = JSON.parse(line) as Record<string, unknown>; } catch { continue; }\n\n if (msg.type === \"status\") {\n extensionConnected = Boolean(msg.connected);\n } else if (msg.type === \"response\") {\n const id = msg.id as string;\n const slot = pending.get(id);\n if (!slot) continue;\n clearTimeout(slot.timer);\n pending.delete(id);\n if (msg.error !== undefined) {\n slot.reject(new Error(msg.error as string));\n } else {\n slot.resolve(msg.result);\n }\n }\n }\n });\n\n sock.on(\"close\", () => {\n hubConnected = false;\n extensionConnected = false;\n for (const [, slot] of pending) {\n clearTimeout(slot.timer);\n slot.reject(new Error(\"Hub disconnected\"));\n }\n pending.clear();\n });\n\n sock.on(\"error\", (err) => {\n process.stderr.write(`[livemcp] Hub connection error: ${err.message}\\n`);\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n process.stderr.write(`[livemcp] Hub not running. Start it with: livemcp-hub\\n`);\n }\n });\n\n const isConnected = () => hubConnected && extensionConnected;\n\n const request = (action: BridgeAction, params: Record<string, unknown> = {}) => {\n if (!hubConnected) {\n return Promise.reject(new Error(\"livemcp-hub is not running. Start it with: livemcp-hub\"));\n }\n if (!extensionConnected) {\n return Promise.reject(new Error(\"Chrome extension not connected to hub\"));\n }\n const id = randomUUID();\n return new Promise<unknown>((resolve, reject) => {\n const timer = setTimeout(() => {\n pending.delete(id);\n reject(new Error(\"Bridge request timed out\"));\n }, requestTimeoutMs);\n pending.set(id, { resolve, reject, timer });\n send({ type: \"request\", sessionId, id, action, params });\n });\n };\n\n const close = async () => {\n if (hubConnected) send({ type: \"unregister\", sessionId });\n for (const [, slot] of pending) {\n clearTimeout(slot.timer);\n slot.reject(new Error(\"Bridge closed\"));\n }\n pending.clear();\n sock.destroy();\n };\n\n return { isConnected, request, close };\n}\n", "import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport type { Bridge } from \"../bridge.js\";\nimport { bridgeCall } from \"./helpers.js\";\n\nexport function registerConsoleTools(mcp: McpServer, bridge: Bridge): void {\n mcp.registerTool(\n \"start_console_capture\",\n {\n description: \"Attach debugger and start recording console log entries for a tab\",\n inputSchema: { tabId: z.number().int().positive() },\n },\n async ({ tabId }) => bridgeCall(bridge, \"console.startCapture\", { tabId }),\n );\n mcp.registerTool(\n \"stop_console_capture\",\n {\n description: \"Stop console recording for the tab\",\n inputSchema: { tabId: z.number().int().positive() },\n },\n async ({ tabId }) => bridgeCall(bridge, \"console.stopCapture\", { tabId }),\n );\n mcp.registerTool(\n \"get_console_logs\",\n {\n description: \"Return captured console entries for a tab\",\n inputSchema: {\n tabId: z.number().int().positive(),\n clearAfter: z.boolean().optional(),\n },\n },\n async (args) =>\n bridgeCall(bridge, \"console.getLogs\", {\n tabId: args.tabId,\n clearAfter: args.clearAfter ?? false,\n }),\n );\n}\n", "import type { Bridge } from \"../bridge.js\";\nimport type { BridgeAction } from \"@livemcp/shared\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport { errText, okJson } from \"../toolResult.js\";\n\nexport const tabSpecSchema = {\n tabId: z.number().int().positive().optional().describe(\"Numeric tab ID (prefer tabUrl/tabTitle \u2014 numeric IDs change when tabs are opened)\"),\n tabUrl: z.string().optional().describe(\"Target the first tab whose URL contains this substring (e.g. 'github.com')\"),\n tabTitle: z.string().optional().describe(\"Target the first tab whose title contains this substring (e.g. 'Dashboard')\"),\n};\n\nexport async function bridgeCall(\n bridge: Bridge,\n action: BridgeAction,\n params: Record<string, unknown> = {},\n): Promise<CallToolResult> {\n try {\n const r = await bridge.request(action, params);\n return okJson(r);\n } catch (e) {\n return errText(e instanceof Error ? e.message : String(e));\n }\n}\n", "import type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\n\nexport function okText(text: string): CallToolResult {\n return { content: [{ type: \"text\", text }] };\n}\n\nexport function okJson(data: unknown): CallToolResult {\n return { content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }] };\n}\n\nexport function errText(message: string): CallToolResult {\n return { content: [{ type: \"text\", text: message }], isError: true };\n}\n", "import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport type { Bridge } from \"../bridge.js\";\nimport { bridgeCall, tabSpecSchema } from \"./helpers.js\";\n\nconst formatSchema = z.enum([\"text\", \"html\", \"markdown\"]);\n\nexport function registerContentTools(mcp: McpServer, bridge: Bridge): void {\n mcp.registerTool(\n \"get_page_content\",\n {\n description: \"Read page content from a tab as text, html, or markdown-oriented plain text\",\n inputSchema: {\n ...tabSpecSchema,\n format: formatSchema.optional(),\n },\n },\n async (args) =>\n bridgeCall(bridge, \"content.getPage\", {\n tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle,\n format: args.format ?? \"text\",\n }),\n );\n mcp.registerTool(\n \"get_selected_text\",\n {\n description: \"Get the current text selection in a tab\",\n inputSchema: { ...tabSpecSchema },\n },\n async (args) => bridgeCall(bridge, \"content.getSelection\", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle }),\n );\n}\n", "import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport type { Bridge } from \"../bridge.js\";\nimport { bridgeCall, tabSpecSchema } from \"./helpers.js\";\n\nexport function registerCookieTools(mcp: McpServer, bridge: Bridge): void {\n mcp.registerTool(\n \"get_cookies\",\n {\n description: \"List cookies for a URL\",\n inputSchema: { url: z.string() },\n },\n async ({ url }) => bridgeCall(bridge, \"cookies.get\", { url }),\n );\n mcp.registerTool(\n \"get_local_storage\",\n {\n description: \"Read localStorage for a tab origin\",\n inputSchema: { ...tabSpecSchema },\n },\n async (args) => bridgeCall(bridge, \"cookies.getLocalStorage\", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle }),\n );\n}\n", "import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport type { Bridge } from \"../bridge.js\";\nimport { bridgeCall, tabSpecSchema } from \"./helpers.js\";\n\nconst fieldSchema = z.object({\n selector: z.string(),\n value: z.string(),\n});\n\nexport function registerInteractTools(mcp: McpServer, bridge: Bridge): void {\n mcp.registerTool(\n \"click_element\",\n {\n description: \"Click the first element matching a CSS selector\",\n inputSchema: {\n selector: z.string(),\n ...tabSpecSchema,\n },\n },\n async (args) =>\n bridgeCall(bridge, \"interact.click\", { selector: args.selector, tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle }),\n );\n mcp.registerTool(\n \"type_text\",\n {\n description: \"Type into an input element matched by selector\",\n inputSchema: {\n selector: z.string(),\n text: z.string(),\n clear: z.boolean().optional(),\n ...tabSpecSchema,\n },\n },\n async (args) =>\n bridgeCall(bridge, \"interact.type\", {\n selector: args.selector,\n text: args.text,\n clear: args.clear ?? false,\n tabId: args.tabId,\n tabUrl: args.tabUrl,\n tabTitle: args.tabTitle,\n }),\n );\n mcp.registerTool(\n \"fill_form\",\n {\n description:\n \"Fill multiple form fields by selector. Supports input, textarea, select (dropdown), checkbox (value=true/false), radio, and contentEditable. Set submit=true to submit the form after filling.\",\n inputSchema: {\n fields: z.array(fieldSchema),\n submit: z.boolean().optional().describe(\"Submit the form after filling all fields\"),\n ...tabSpecSchema,\n },\n },\n async (args) =>\n bridgeCall(bridge, \"interact.fillForm\", {\n fields: args.fields,\n submit: args.submit ?? false,\n tabId: args.tabId,\n tabUrl: args.tabUrl,\n tabTitle: args.tabTitle,\n }),\n );\n mcp.registerTool(\n \"click_and_wait\",\n {\n description:\n \"Click an element and wait for a result. Use waitForNavigation=true when the click triggers a page navigation. Use waitFor=selector to wait for a specific element to appear (e.g. a modal or result). Saves an extra round-trip vs click_element + polling.\",\n inputSchema: {\n selector: z.string(),\n waitForNavigation: z.boolean().optional().describe(\"Wait for page navigation to complete\"),\n waitFor: z.string().optional().describe(\"CSS selector to wait for after clicking\"),\n timeout: z.number().int().positive().optional().describe(\"Timeout in ms (default 5000)\"),\n ...tabSpecSchema,\n },\n },\n async (args) =>\n bridgeCall(bridge, \"interact.clickAndWait\", {\n selector: args.selector,\n waitForNavigation: args.waitForNavigation ?? false,\n waitFor: args.waitFor,\n timeout: args.timeout,\n tabId: args.tabId,\n tabUrl: args.tabUrl,\n tabTitle: args.tabTitle,\n }),\n );\n mcp.registerTool(\n \"scroll_page\",\n {\n description: \"Scroll the page by direction\",\n inputSchema: {\n direction: z.enum([\"up\", \"down\", \"left\", \"right\"]),\n amount: z.number().int().positive().optional(),\n ...tabSpecSchema,\n },\n },\n async (args) =>\n bridgeCall(bridge, \"interact.scroll\", {\n direction: args.direction,\n amount: args.amount ?? 400,\n tabId: args.tabId,\n tabUrl: args.tabUrl,\n tabTitle: args.tabTitle,\n }),\n );\n}\n", "import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport type { Bridge } from \"../bridge.js\";\nimport { bridgeCall, tabSpecSchema } from \"./helpers.js\";\n\nexport function registerNavigateTools(mcp: McpServer, bridge: Bridge): void {\n mcp.registerTool(\n \"navigate_to\",\n {\n description: \"Navigate a tab to a URL\",\n inputSchema: {\n url: z.string(),\n ...tabSpecSchema,\n },\n },\n async (args) => bridgeCall(bridge, \"navigate.to\", { url: args.url, tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle }),\n );\n mcp.registerTool(\n \"go_back\",\n {\n description: \"History back for a tab\",\n inputSchema: { ...tabSpecSchema },\n },\n async (args) => bridgeCall(bridge, \"navigate.back\", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle }),\n );\n mcp.registerTool(\n \"go_forward\",\n {\n description: \"History forward for a tab\",\n inputSchema: { ...tabSpecSchema },\n },\n async (args) => bridgeCall(bridge, \"navigate.forward\", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle }),\n );\n mcp.registerTool(\n \"reload_tab\",\n {\n description: \"Reload a tab\",\n inputSchema: { ...tabSpecSchema },\n },\n async (args) => bridgeCall(bridge, \"navigate.reload\", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle }),\n );\n mcp.registerTool(\n \"navigate_and_wait\",\n {\n description:\n \"Navigate to a URL and wait for the page to fully load. Optionally wait for a specific CSS selector to appear after load. Faster than navigate_to + polling for content.\",\n inputSchema: {\n url: z.string(),\n waitFor: z.string().optional().describe(\"CSS selector to wait for after page load\"),\n timeout: z.number().int().positive().optional().describe(\"Timeout in ms (default 10000)\"),\n ...tabSpecSchema,\n },\n },\n async (args) =>\n bridgeCall(bridge, \"navigate.andWait\", {\n url: args.url,\n waitFor: args.waitFor,\n timeout: args.timeout,\n tabId: args.tabId,\n tabUrl: args.tabUrl,\n tabTitle: args.tabTitle,\n }),\n );\n}\n", "import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport type { Bridge } from \"../bridge.js\";\nimport { bridgeCall } from \"./helpers.js\";\n\nexport function registerNetworkTools(mcp: McpServer, bridge: Bridge): void {\n mcp.registerTool(\n \"start_network_capture\",\n {\n description: \"Attach debugger and start recording network requests for a tab\",\n inputSchema: { tabId: z.number().int().positive() },\n },\n async ({ tabId }) => bridgeCall(bridge, \"network.startCapture\", { tabId }),\n );\n mcp.registerTool(\n \"stop_network_capture\",\n {\n description: \"Stop network recording and detach debugger for that capture on the tab\",\n inputSchema: { tabId: z.number().int().positive() },\n },\n async ({ tabId }) => bridgeCall(bridge, \"network.stopCapture\", { tabId }),\n );\n mcp.registerTool(\n \"get_captured_requests\",\n {\n description: \"Return captured HTTP requests for a tab\",\n inputSchema: {\n tabId: z.number().int().positive(),\n clearAfter: z.boolean().optional(),\n },\n },\n async (args) =>\n bridgeCall(bridge, \"network.getCaptured\", {\n tabId: args.tabId,\n clearAfter: args.clearAfter ?? false,\n }),\n );\n}\n", "import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Bridge } from \"../bridge.js\";\nimport { bridgeCall, tabSpecSchema } from \"./helpers.js\";\n\nexport function registerScreenshotTools(mcp: McpServer, bridge: Bridge): void {\n mcp.registerTool(\n \"take_screenshot\",\n {\n description: \"Capture the visible area of a tab as PNG base64 data URL\",\n inputSchema: { ...tabSpecSchema },\n },\n async (args) => bridgeCall(bridge, \"screenshot.capture\", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle }),\n );\n}\n", "import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Bridge } from \"../bridge.js\";\nimport { tabSpecSchema } from \"./helpers.js\";\nimport { errText } from \"../toolResult.js\";\n\ntype SnapshotResult = {\n url: string;\n title: string;\n screenshot: string;\n interactive: unknown[];\n headings: string[];\n};\n\nexport function registerSnapshotTools(mcp: McpServer, bridge: Bridge): void {\n mcp.registerTool(\n \"get_page_snapshot\",\n {\n description:\n \"Get a full page snapshot in one call: visual screenshot + interactive elements (inputs, buttons, links with selectors) + headings + URL. Use this instead of separate screenshot + get_page_content calls to save round-trips.\",\n inputSchema: { ...tabSpecSchema },\n },\n async (args) => {\n try {\n const result = (await bridge.request(\"page.snapshot\", { tabId: args.tabId, tabUrl: args.tabUrl, tabTitle: args.tabTitle })) as SnapshotResult;\n const base64 = result.screenshot.replace(/^data:image\\/[^;]+;base64,/, \"\");\n return {\n content: [\n { type: \"image\" as const, data: base64, mimeType: \"image/png\" as const },\n {\n type: \"text\" as const,\n text: JSON.stringify(\n {\n url: result.url,\n title: result.title,\n headings: result.headings,\n interactive: result.interactive,\n },\n null,\n 2,\n ),\n },\n ],\n };\n } catch (e) {\n return errText(e instanceof Error ? e.message : String(e));\n }\n },\n );\n}\n", "import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport type { Bridge } from \"../bridge.js\";\nimport { bridgeCall } from \"./helpers.js\";\n\nexport function registerTabTools(mcp: McpServer, bridge: Bridge): void {\n mcp.registerTool(\n \"list_tabs\",\n { description: \"List open tabs in the user Chrome profile\", inputSchema: z.object({}) },\n async () => bridgeCall(bridge, \"tabs.list\", {}),\n );\n mcp.registerTool(\n \"get_active_tab\",\n { description: \"Get the active tab in the current window\", inputSchema: z.object({}) },\n async () => bridgeCall(bridge, \"tabs.getActive\", {}),\n );\n mcp.registerTool(\n \"switch_tab\",\n {\n description: \"Focus a tab by id\",\n inputSchema: { tabId: z.number().int().positive() },\n },\n async ({ tabId }) => bridgeCall(bridge, \"tabs.switch\", { tabId }),\n );\n mcp.registerTool(\n \"close_tab\",\n {\n description: \"Close a tab by id\",\n inputSchema: { tabId: z.number().int().positive() },\n },\n async ({ tabId }) => bridgeCall(bridge, \"tabs.close\", { tabId }),\n );\n mcp.registerTool(\n \"create_tab\",\n {\n description: \"Open a new tab with optional URL\",\n inputSchema: { url: z.string().min(1).optional() },\n },\n async ({ url }) => bridgeCall(bridge, \"tabs.create\", url ? { url } : {}),\n );\n}\n", "import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Bridge } from \"../bridge.js\";\nimport { registerConsoleTools } from \"./console.js\";\nimport { registerContentTools } from \"./content.js\";\nimport { registerCookieTools } from \"./cookies.js\";\nimport { registerInteractTools } from \"./interact.js\";\nimport { registerNavigateTools } from \"./navigate.js\";\nimport { registerNetworkTools } from \"./network.js\";\nimport { registerScreenshotTools } from \"./screenshot.js\";\nimport { registerSnapshotTools } from \"./snapshot.js\";\nimport { registerTabTools } from \"./tabs.js\";\n\nexport function registerAllTools(mcp: McpServer, bridge: Bridge): void {\n registerTabTools(mcp, bridge);\n registerContentTools(mcp, bridge);\n registerScreenshotTools(mcp, bridge);\n registerSnapshotTools(mcp, bridge);\n registerNavigateTools(mcp, bridge);\n registerNetworkTools(mcp, bridge);\n registerConsoleTools(mcp, bridge);\n registerInteractTools(mcp, bridge);\n registerCookieTools(mcp, bridge);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["z", "bridge", "mcp", "bridge", "z", "z", "z", "mcp", "bridge", "z", "mcp", "bridge", "z", "z", "z", "mcp", "bridge", "z", "mcp", "bridge", "z", "z", "mcp", "bridge", "z", "mcp", "bridge", "mcp", "bridge", "z", "mcp", "bridge", "z", "mcp", "bridge"]
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "livemcp",
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "MCP server that gives AI direct access to your real browser — tabs, cookies, session state, and all.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Deepak Silaych",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/DeepakSilaych/chrome-mcp.git",
|
|
11
|
+
"directory": "server"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"chrome",
|
|
16
|
+
"browser",
|
|
17
|
+
"ai",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"cursor"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"compile": "node scripts/bundle.mjs"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"livemcp": "./dist/index.js",
|
|
26
|
+
"livemcp-hub": "./dist/hub.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
34
|
+
"ws": "^8.18.0",
|
|
35
|
+
"zod": "^3.24.2"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@livemcp/shared": "*",
|
|
39
|
+
"@types/node": "^22.13.5",
|
|
40
|
+
"@types/ws": "^8.5.14",
|
|
41
|
+
"esbuild": "^0.25.0",
|
|
42
|
+
"typescript": "^5.7.3"
|
|
43
|
+
}
|
|
44
|
+
}
|