agentbridgelens 0.1.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 +39 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +308 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# agentbridgelens
|
|
2
|
+
|
|
3
|
+
MCP server that bridges AI coding agents (Claude Code, etc.) to a **real Chrome browser** via the AgentBridgeLens extension: DOM inspection, screenshots (incl. background tabs), network/console capture, source-map tracing (UI → component source line), visual overlays, action record/replay, and multi-tab targeting.
|
|
4
|
+
|
|
5
|
+
It runs over MCP stdio and relays tool calls to the browser extension over a local WebSocket.
|
|
6
|
+
|
|
7
|
+
## Install (recommended: once, globally)
|
|
8
|
+
|
|
9
|
+
No clone/build needed — run straight from npm via `npx`, registered at **user scope** so every project/session has it:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
claude mcp add -s user bridgelens -- npx -y agentbridgelens
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or add it to your MCP config manually:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"mcpServers": {
|
|
20
|
+
"bridgelens": { "command": "npx", "args": ["-y", "agentbridgelens"] }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then install the **AgentBridgeLens Chrome extension** and open any page. The extension auto-connects to the bridge on `127.0.0.1:19222`.
|
|
26
|
+
|
|
27
|
+
## Environment variables
|
|
28
|
+
|
|
29
|
+
| Var | Default | Purpose |
|
|
30
|
+
|-----|---------|---------|
|
|
31
|
+
| `BRIDGELENS_PORT` | `19222` | WebSocket port |
|
|
32
|
+
| `BRIDGELENS_HOST` | `127.0.0.1` | Bind address. Set `0.0.0.0` for cross-machine (extension on another machine). |
|
|
33
|
+
| `BRIDGELENS_TOKEN` | _(none)_ | Shared secret required from the extension. **Strongly recommended whenever HOST ≠ 127.0.0.1.** |
|
|
34
|
+
|
|
35
|
+
Single machine needs no env vars. For cross-machine, prefer an SSH tunnel (`ssh -L 19222:127.0.0.1:19222 …`) and keep the bridge on loopback.
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
|
|
6
|
+
// src/ws-relay.ts
|
|
7
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
8
|
+
var WsRelay = class {
|
|
9
|
+
wss;
|
|
10
|
+
client = null;
|
|
11
|
+
token;
|
|
12
|
+
pending = /* @__PURE__ */ new Map();
|
|
13
|
+
constructor(options) {
|
|
14
|
+
const host = options.host || "127.0.0.1";
|
|
15
|
+
this.token = options.token;
|
|
16
|
+
this.wss = new WebSocketServer({
|
|
17
|
+
port: options.port,
|
|
18
|
+
host,
|
|
19
|
+
// Reject unauthorized clients during the HTTP upgrade, before the WS
|
|
20
|
+
// handshake completes — so a bad token never reaches an open socket.
|
|
21
|
+
verifyClient: (info, done) => {
|
|
22
|
+
if (this.authorize(info.req)) return done(true);
|
|
23
|
+
console.error(`[BridgeLens] Rejected connection from ${info.req.socket.remoteAddress}: invalid token`);
|
|
24
|
+
done(false, 401, "Unauthorized");
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
this.wss.on("connection", (ws, req) => {
|
|
28
|
+
this.client = ws;
|
|
29
|
+
console.error(`[BridgeLens] Extension connected from ${req.socket.remoteAddress}`);
|
|
30
|
+
const heartbeat = setInterval(() => {
|
|
31
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
32
|
+
ws.send(JSON.stringify({ t: "ping" }));
|
|
33
|
+
}
|
|
34
|
+
}, 2e4);
|
|
35
|
+
ws.on("message", (raw) => {
|
|
36
|
+
const msg = JSON.parse(raw.toString());
|
|
37
|
+
const pending = this.pending.get(msg.id);
|
|
38
|
+
if (!pending) return;
|
|
39
|
+
this.pending.delete(msg.id);
|
|
40
|
+
if (msg.error) {
|
|
41
|
+
pending.reject(new Error(msg.error.message));
|
|
42
|
+
} else {
|
|
43
|
+
pending.resolve(msg.result);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
ws.on("close", () => {
|
|
47
|
+
clearInterval(heartbeat);
|
|
48
|
+
this.client = null;
|
|
49
|
+
console.error(`[BridgeLens] Extension disconnected`);
|
|
50
|
+
for (const [id, { reject }] of this.pending) {
|
|
51
|
+
reject(new Error("Extension disconnected"));
|
|
52
|
+
this.pending.delete(id);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
const authNote = this.token ? "token required" : "no token";
|
|
57
|
+
if (host !== "127.0.0.1" && !this.token) {
|
|
58
|
+
console.error(
|
|
59
|
+
`[BridgeLens] WARNING: listening on ${host}:${options.port} with no BRIDGELENS_TOKEN \u2014 the browser-control channel is exposed to the network unauthenticated.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
console.error(`[BridgeLens] WebSocket server listening on ${host}:${options.port} (${authNote})`);
|
|
63
|
+
}
|
|
64
|
+
/** Validate the connecting extension's ?token= query against the configured token. */
|
|
65
|
+
authorize(req) {
|
|
66
|
+
if (!this.token) return true;
|
|
67
|
+
let provided = null;
|
|
68
|
+
try {
|
|
69
|
+
provided = new URL(req.url ?? "/", "ws://localhost").searchParams.get("token");
|
|
70
|
+
} catch {
|
|
71
|
+
provided = null;
|
|
72
|
+
}
|
|
73
|
+
return provided === this.token;
|
|
74
|
+
}
|
|
75
|
+
get connected() {
|
|
76
|
+
return this.client?.readyState === WebSocket.OPEN;
|
|
77
|
+
}
|
|
78
|
+
async send(tool, params) {
|
|
79
|
+
if (!this.client || this.client.readyState !== WebSocket.OPEN) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
"Chrome extension not connected. Please open Chrome with the AgentBridgeLens extension installed."
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
const id = crypto.randomUUID();
|
|
85
|
+
const request = { id, tool, params };
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
const timeout = setTimeout(() => {
|
|
88
|
+
this.pending.delete(id);
|
|
89
|
+
reject(new Error(`Tool '${tool}' timed out after 120s`));
|
|
90
|
+
}, 12e4);
|
|
91
|
+
this.pending.set(id, {
|
|
92
|
+
resolve: (v) => {
|
|
93
|
+
clearTimeout(timeout);
|
|
94
|
+
resolve(v);
|
|
95
|
+
},
|
|
96
|
+
reject: (e) => {
|
|
97
|
+
clearTimeout(timeout);
|
|
98
|
+
reject(e);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
this.client.send(JSON.stringify(request));
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
close() {
|
|
105
|
+
this.wss.close();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// src/mcp-server.ts
|
|
110
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
111
|
+
import { z } from "zod";
|
|
112
|
+
|
|
113
|
+
// src/error-tracer.ts
|
|
114
|
+
import { TraceMap, originalPositionFor } from "@jridgewell/trace-mapping";
|
|
115
|
+
function parseStack(stack) {
|
|
116
|
+
const frames = [];
|
|
117
|
+
const re = /(https?:\/\/[^\s)]+?):(\d+):(\d+)/;
|
|
118
|
+
for (const line of stack.split("\n")) {
|
|
119
|
+
const m = re.exec(line);
|
|
120
|
+
if (m) {
|
|
121
|
+
frames.push({
|
|
122
|
+
url: m[1],
|
|
123
|
+
line: parseInt(m[2], 10),
|
|
124
|
+
column: parseInt(m[3], 10),
|
|
125
|
+
raw: line.trim()
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return frames;
|
|
130
|
+
}
|
|
131
|
+
async function loadTraceMap(jsUrl) {
|
|
132
|
+
const res = await fetch(jsUrl);
|
|
133
|
+
if (!res.ok) return null;
|
|
134
|
+
const js = await res.text();
|
|
135
|
+
const m = /\/\/[#@]\s*sourceMappingURL=(.+?)\s*$/m.exec(js);
|
|
136
|
+
if (!m) return null;
|
|
137
|
+
const mapRef = m[1].trim();
|
|
138
|
+
let rawMap;
|
|
139
|
+
if (mapRef.startsWith("data:")) {
|
|
140
|
+
const comma = mapRef.indexOf(",");
|
|
141
|
+
const meta = mapRef.slice(0, comma);
|
|
142
|
+
const payload = mapRef.slice(comma + 1);
|
|
143
|
+
rawMap = meta.includes("base64") ? Buffer.from(payload, "base64").toString("utf8") : decodeURIComponent(payload);
|
|
144
|
+
} else {
|
|
145
|
+
const abs = new URL(mapRef, jsUrl).href;
|
|
146
|
+
const mr = await fetch(abs);
|
|
147
|
+
if (!mr.ok) return null;
|
|
148
|
+
rawMap = await mr.text();
|
|
149
|
+
}
|
|
150
|
+
return new TraceMap(JSON.parse(rawMap));
|
|
151
|
+
}
|
|
152
|
+
async function traceErrorToSource(stack) {
|
|
153
|
+
const frames = parseStack(stack);
|
|
154
|
+
if (frames.length === 0) {
|
|
155
|
+
return {
|
|
156
|
+
error: "\u672A\u4ECE\u5806\u6808\u4E2D\u89E3\u6790\u51FA url:line:column \u5F62\u5F0F\u7684\u5E27",
|
|
157
|
+
hint: "\u8BF7\u4F20\u5165\u5305\u542B\u5B8C\u6574 URL \u7684\u5806\u6808\uFF08\u5982 Chrome \u63A7\u5236\u53F0\u91CC Error.stack \u7684\u5185\u5BB9\uFF09"
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const cache = /* @__PURE__ */ new Map();
|
|
161
|
+
const resolved = [];
|
|
162
|
+
for (const f of frames) {
|
|
163
|
+
if (!cache.has(f.url)) {
|
|
164
|
+
try {
|
|
165
|
+
cache.set(f.url, await loadTraceMap(f.url));
|
|
166
|
+
} catch {
|
|
167
|
+
cache.set(f.url, null);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const map = cache.get(f.url) ?? null;
|
|
171
|
+
if (map) {
|
|
172
|
+
const pos = originalPositionFor(map, { line: f.line, column: f.column });
|
|
173
|
+
resolved.push({
|
|
174
|
+
raw: f.raw,
|
|
175
|
+
source: pos.source,
|
|
176
|
+
line: pos.line,
|
|
177
|
+
column: pos.column,
|
|
178
|
+
name: pos.name
|
|
179
|
+
});
|
|
180
|
+
} else {
|
|
181
|
+
resolved.push({
|
|
182
|
+
raw: f.raw,
|
|
183
|
+
note: "\u65E0 source map\uFF08\u751F\u4EA7\u6784\u5EFA\u672A\u53D1\u5E03 .map\uFF0C\u6216\u6587\u4EF6\u4E0D\u53EF\u8BBF\u95EE\uFF09",
|
|
184
|
+
url: f.url,
|
|
185
|
+
line: f.line,
|
|
186
|
+
column: f.column
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return { frames: resolved };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/mcp-server.ts
|
|
194
|
+
function createMcpServer(relay2) {
|
|
195
|
+
const server = new McpServer({ name: "AgentBridgeLens", version: "0.1.0" });
|
|
196
|
+
const TAB = {
|
|
197
|
+
tabId: z.number().optional().describe("\u76EE\u6807\u6807\u7B7E\u9875 id\uFF08\u6765\u81EA list_tabs\uFF09\u3002\u7701\u7565\u5219\u7528\u5DF2\u56FA\u5B9A\u7684\u76EE\u6807\u6807\u7B7E\u9875\uFF0C\u5426\u5219\u7528\u5F53\u524D\u6FC0\u6D3B\u6807\u7B7E\u9875")
|
|
198
|
+
};
|
|
199
|
+
const asText = (result) => ({
|
|
200
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
201
|
+
});
|
|
202
|
+
const page = (name, description, schema = {}) => server.tool(name, description, { ...schema, ...TAB }, async (args) => asText(await relay2.send(name, args)));
|
|
203
|
+
const plain = (name, description, schema = {}) => server.tool(name, description, schema, async (args) => asText(await relay2.send(name, args)));
|
|
204
|
+
plain("list_tabs", "List all open tabs across all windows (tabId/title/url/active/isTarget/openedByTarget). Multiple tabs can be pinned as targets; operate on any tab by passing its tabId.");
|
|
205
|
+
plain("set_target_tab", "Pin a tab into the target set (multiple tabs can be pinned). When a tool is called without tabId, the most-recently-active pinned tab is used.", {
|
|
206
|
+
tabId: z.number().describe("Tab id to pin (from list_tabs)")
|
|
207
|
+
});
|
|
208
|
+
plain("get_target_tab", "Get the set of pinned target tabs and which one is the current default (follow-active if none pinned).");
|
|
209
|
+
plain("clear_target_tab", "Unpin a target tab, or clear all pinned targets if tabId is omitted.", {
|
|
210
|
+
tabId: z.number().optional().describe("Tab id to unpin. Omit to clear all.")
|
|
211
|
+
});
|
|
212
|
+
page("get_page_info", "Get current page URL, title, and meta information");
|
|
213
|
+
server.tool(
|
|
214
|
+
"capture_screenshot",
|
|
215
|
+
"Capture a screenshot of a tab, or crop to a specific element. Background tabs are captured via CDP.",
|
|
216
|
+
{
|
|
217
|
+
selector: z.string().optional().describe("CSS selector to capture a specific element (scrolled into view and cropped). Omit for full viewport."),
|
|
218
|
+
...TAB
|
|
219
|
+
},
|
|
220
|
+
async (args) => {
|
|
221
|
+
const result = await relay2.send("capture_screenshot", args);
|
|
222
|
+
const base64 = result.dataUrl.replace(/^data:image\/png;base64,/, "");
|
|
223
|
+
return { content: [{ type: "image", data: base64, mimeType: "image/png" }] };
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
page("execute_js", "Execute a JavaScript expression in the page context and return the result (falls back to CDP on strict-CSP pages)", {
|
|
227
|
+
code: z.string().describe("JavaScript code to execute in the page context")
|
|
228
|
+
});
|
|
229
|
+
page("get_dom_snapshot", "Get a simplified DOM snapshot of the page or a specific subtree", {
|
|
230
|
+
selector: z.string().optional().describe("CSS selector for subtree root. Omit for full page."),
|
|
231
|
+
maxDepth: z.number().optional().describe("Maximum depth of DOM tree to capture. Default 6.")
|
|
232
|
+
});
|
|
233
|
+
page("inspect_element", "Inspect a DOM element: attributes, computed styles, box model, and text content", {
|
|
234
|
+
selector: z.string().describe("CSS selector of the element to inspect")
|
|
235
|
+
});
|
|
236
|
+
page("get_accessibility_tree", "Get a simplified accessibility tree (roles + accessible names) of the page or a subtree", {
|
|
237
|
+
selector: z.string().optional().describe("CSS selector for subtree root. Omit for full page."),
|
|
238
|
+
maxDepth: z.number().optional().describe("Maximum depth to traverse. Default 12.")
|
|
239
|
+
});
|
|
240
|
+
page("get_performance_metrics", "Get page performance metrics: navigation timing, FCP, LCP, CLS, JS heap memory");
|
|
241
|
+
page("trace_element_to_source", "Trace a DOM element back to its source component file and line number (React/Vue dev builds)", {
|
|
242
|
+
selector: z.string().describe("CSS selector of the element to trace")
|
|
243
|
+
});
|
|
244
|
+
page("trace_style_to_source", "Trace which CSS rules apply to an element and which stylesheet they come from", {
|
|
245
|
+
selector: z.string().describe("CSS selector of the element to trace"),
|
|
246
|
+
property: z.string().optional().describe("Only return rules that set this CSS property")
|
|
247
|
+
});
|
|
248
|
+
server.tool(
|
|
249
|
+
"trace_error_to_source",
|
|
250
|
+
"Resolve a JS error stack trace back to original source files/lines via source maps (runs in the bridge, fetches the bundle and .map)",
|
|
251
|
+
{ stack: z.string().describe("The error stack string (e.g. from Error.stack or get_errors output)") },
|
|
252
|
+
async ({ stack }) => asText(await traceErrorToSource(stack))
|
|
253
|
+
);
|
|
254
|
+
page("get_console_logs", "Get captured console log entries from the page", {
|
|
255
|
+
level: z.enum(["log", "warn", "error", "info", "debug"]).optional().describe("Filter by log level"),
|
|
256
|
+
limit: z.number().optional().describe("Maximum number of entries to return. Default 50.")
|
|
257
|
+
});
|
|
258
|
+
page("get_errors", "Get only error-level console entries (uncaught errors and rejections included)");
|
|
259
|
+
page("get_network_requests", "Get network requests captured (lightweight; only those after content-script injection)", {
|
|
260
|
+
urlPattern: z.string().optional().describe("Filter by URL substring"),
|
|
261
|
+
status: z.number().optional().describe("Filter by HTTP status code")
|
|
262
|
+
});
|
|
263
|
+
page("start_cdp_network", "Attach the Chrome debugger to a tab and start capturing full network traffic via CDP. Shows a debugging banner; works per-tab (multiple tabs supported).");
|
|
264
|
+
page("stop_cdp_network", "Detach the Chrome debugger and stop CDP network capture for a tab");
|
|
265
|
+
page("get_cdp_network", "Get network requests captured via CDP for a tab (requires start_cdp_network first)", {
|
|
266
|
+
urlPattern: z.string().optional().describe("Filter by URL substring"),
|
|
267
|
+
status: z.number().optional().describe("Filter by HTTP status code")
|
|
268
|
+
});
|
|
269
|
+
page("mark_elements", "Mark/annotate a set of elements with colored boxes (e.g. to flag changed regions)", {
|
|
270
|
+
selectors: z.array(z.string()).describe("CSS selectors of elements to mark"),
|
|
271
|
+
color: z.string().optional().describe("Box color (CSS color). Default '#ff3b30'"),
|
|
272
|
+
label: z.string().optional().describe("Optional label text shown on each box")
|
|
273
|
+
});
|
|
274
|
+
page("visualize_layout", "Outline elements whose content is clipped by overflow (layout debugging)");
|
|
275
|
+
page("show_responsive_frame", "Overlay a target-viewport-width frame and list elements wider than it (responsive debugging)", {
|
|
276
|
+
width: z.number().optional().describe("Target viewport width in px. Default 375.")
|
|
277
|
+
});
|
|
278
|
+
page("clear_overlays", "Remove all visual overlays (marks / layout / responsive frame)");
|
|
279
|
+
page("start_recording", "Start recording user interactions (clicks, inputs, scrolls) on a tab");
|
|
280
|
+
page("stop_recording", "Stop recording and return the captured action sequence");
|
|
281
|
+
page("replay_actions", "Replay a previously recorded action sequence on a tab", {
|
|
282
|
+
actions: z.array(z.record(z.string(), z.unknown())).describe("Action sequence, as returned by stop_recording")
|
|
283
|
+
});
|
|
284
|
+
page("show_hud", "Show a small on-page HUD panel in the bottom-right corner");
|
|
285
|
+
page("hide_hud", "Hide the on-page HUD panel");
|
|
286
|
+
page("update_hud", "Update the HUD status text", {
|
|
287
|
+
text: z.string().describe("Status text to display in the HUD")
|
|
288
|
+
});
|
|
289
|
+
plain(
|
|
290
|
+
"request_user_confirmation",
|
|
291
|
+
"Ask the user a question in the side panel and wait for their choice (the AgentBridgeLens side panel must be open).",
|
|
292
|
+
{
|
|
293
|
+
message: z.string().describe("The question/prompt shown to the user"),
|
|
294
|
+
options: z.array(z.string()).optional().describe("Choice buttons. Default ['\u786E\u8BA4', '\u53D6\u6D88']")
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
return server;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/index.ts
|
|
301
|
+
var WS_PORT = parseInt(process.env.BRIDGELENS_PORT || "19222", 10);
|
|
302
|
+
var WS_HOST = process.env.BRIDGELENS_HOST || "127.0.0.1";
|
|
303
|
+
var WS_TOKEN = process.env.BRIDGELENS_TOKEN || void 0;
|
|
304
|
+
var relay = new WsRelay({ port: WS_PORT, host: WS_HOST, token: WS_TOKEN });
|
|
305
|
+
var mcpServer = createMcpServer(relay);
|
|
306
|
+
var transport = new StdioServerTransport();
|
|
307
|
+
await mcpServer.connect(transport);
|
|
308
|
+
console.error(`[BridgeLens] MCP server started (WS ${WS_HOST}:${WS_PORT})`);
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agentbridgelens",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server bridging AI coding agents to a real Chrome browser via the AgentBridgeLens extension — DOM inspection, screenshots, network/console capture, source-map tracing, visual overlays, multi-tab targeting.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agentbridgelens": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=18"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"model-context-protocol",
|
|
18
|
+
"claude",
|
|
19
|
+
"ai-agent",
|
|
20
|
+
"chrome-extension",
|
|
21
|
+
"browser",
|
|
22
|
+
"cdp",
|
|
23
|
+
"debugging",
|
|
24
|
+
"source-map"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/Erhao/AgentBridgeLens.git",
|
|
30
|
+
"directory": "packages/bridge"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/Erhao/AgentBridgeLens#readme",
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
35
|
+
"dev": "tsup src/index.ts --format esm --watch --onSuccess 'node dist/index.js'",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"prepublishOnly": "tsup src/index.ts --format esm --dts"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@jridgewell/trace-mapping": "^0.3.25",
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
42
|
+
"ws": "^8.18.0",
|
|
43
|
+
"zod": "^3.25.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^22.15.0",
|
|
47
|
+
"@types/ws": "^8.18.0",
|
|
48
|
+
"tsup": "^8.4.0",
|
|
49
|
+
"typescript": "^5.8.0"
|
|
50
|
+
}
|
|
51
|
+
}
|