@mehmoodqureshi/chrome-mcp 0.6.7 → 0.7.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 +116 -0
- package/dist/shared/observers.d.ts +110 -0
- package/dist/shared/observers.js +127 -0
- package/dist/shared/page-fns.d.ts +46 -0
- package/dist/shared/page-fns.js +292 -0
- package/dist/shared/policy.d.ts +9 -0
- package/dist/shared/policy.js +16 -0
- package/dist/shared/protocol.d.ts +11 -2
- package/dist/shared/protocol.js +3 -0
- package/dist/shared/snapshot.d.ts +2 -0
- package/dist/shared/snapshot.js +8 -1
- package/dist/src/bridge/workspace.d.ts +6 -0
- package/dist/src/bridge/workspace.js +20 -0
- package/dist/src/config.js +31 -0
- package/dist/src/executor/extension-executor.d.ts +27 -13
- package/dist/src/executor/extension-executor.js +53 -12
- package/dist/src/executor/stub-executor.d.ts +19 -1
- package/dist/src/executor/stub-executor.js +22 -6
- package/dist/src/executor/types.d.ts +72 -13
- package/dist/src/mcp/audit.d.ts +39 -0
- package/dist/src/mcp/audit.js +60 -0
- package/dist/src/mcp/helpers.d.ts +4 -4
- package/dist/src/mcp/helpers.js +9 -4
- package/dist/src/mcp/locate.d.ts +45 -0
- package/dist/src/mcp/locate.js +105 -0
- package/dist/src/mcp/redact.d.ts +48 -0
- package/dist/src/mcp/redact.js +92 -0
- package/dist/src/mcp/snapdiff.d.ts +43 -0
- package/dist/src/mcp/snapdiff.js +92 -0
- package/dist/src/mcp/tools.js +429 -43
- package/dist/src/security/policy.d.ts +5 -0
- package/dist/src/security/policy.js +6 -0
- package/docs/BLUEPRINT.md +15 -1
- package/extension-dist/background.js +617 -214
- package/extension-dist/page-hook.js +215 -0
- package/package.json +1 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
// shared/observers.ts
|
|
4
|
+
var OBSERVER_HOOK_VERSION = 1;
|
|
5
|
+
var OBSERVER_GLOBAL = "__chromeMcpObservers";
|
|
6
|
+
var MAX_CONSOLE_ENTRIES = 500;
|
|
7
|
+
var MAX_NETWORK_ENTRIES = 300;
|
|
8
|
+
var MAX_DIALOG_ENTRIES = 50;
|
|
9
|
+
|
|
10
|
+
// extension/src/page/hook.ts
|
|
11
|
+
(function install() {
|
|
12
|
+
try {
|
|
13
|
+
const w = window;
|
|
14
|
+
if (w[OBSERVER_GLOBAL]) return;
|
|
15
|
+
const state = {
|
|
16
|
+
v: OBSERVER_HOOK_VERSION,
|
|
17
|
+
seq: 0,
|
|
18
|
+
console: [],
|
|
19
|
+
network: [],
|
|
20
|
+
dialogs: [],
|
|
21
|
+
dialogPolicy: "dismiss",
|
|
22
|
+
promptText: "",
|
|
23
|
+
dropped: false,
|
|
24
|
+
resourceCursor: 0
|
|
25
|
+
};
|
|
26
|
+
Object.defineProperty(w, OBSERVER_GLOBAL, { value: state, writable: true, enumerable: false, configurable: true });
|
|
27
|
+
const push = (buf, entry, max) => {
|
|
28
|
+
buf.push(entry);
|
|
29
|
+
if (buf.length > max) {
|
|
30
|
+
buf.splice(0, buf.length - max);
|
|
31
|
+
state.dropped = true;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const render = (value, depth = 0) => {
|
|
35
|
+
try {
|
|
36
|
+
if (value === null) return "null";
|
|
37
|
+
if (value === void 0) return "undefined";
|
|
38
|
+
const t = typeof value;
|
|
39
|
+
if (t === "string") return value;
|
|
40
|
+
if (t === "number" || t === "boolean" || t === "bigint") return String(value);
|
|
41
|
+
if (t === "function") return `[Function ${value.name || "anonymous"}]`;
|
|
42
|
+
if (value instanceof Error) return `${value.name}: ${value.message}`;
|
|
43
|
+
if (value instanceof Element) return `<${value.tagName.toLowerCase()}>`;
|
|
44
|
+
if (depth >= 2) return Array.isArray(value) ? "[Array]" : "[Object]";
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
return `[${value.slice(0, 20).map((v) => render(v, depth + 1)).join(", ")}${value.length > 20 ? ", \u2026" : ""}]`;
|
|
47
|
+
}
|
|
48
|
+
const entries = Object.entries(value).slice(0, 20);
|
|
49
|
+
return `{${entries.map(([k, v]) => `${k}: ${render(v, depth + 1)}`).join(", ")}}`;
|
|
50
|
+
} catch {
|
|
51
|
+
return "[unrenderable]";
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const CAP = 2e3;
|
|
55
|
+
const clamp = (s) => s.length > CAP ? `${s.slice(0, CAP)}\u2026[${s.length} chars]` : s;
|
|
56
|
+
const record = (level, text, extra) => {
|
|
57
|
+
push(
|
|
58
|
+
state.console,
|
|
59
|
+
{
|
|
60
|
+
seq: ++state.seq,
|
|
61
|
+
ts: Date.now(),
|
|
62
|
+
level,
|
|
63
|
+
text: clamp(text),
|
|
64
|
+
...extra?.stack ? { stack: clamp(extra.stack) } : {},
|
|
65
|
+
url: location.href
|
|
66
|
+
},
|
|
67
|
+
MAX_CONSOLE_ENTRIES
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
const LEVELS = ["log", "info", "warn", "error", "debug"];
|
|
71
|
+
for (const level of LEVELS) {
|
|
72
|
+
const original = console[level];
|
|
73
|
+
if (typeof original !== "function") continue;
|
|
74
|
+
console[level] = function patched(...args) {
|
|
75
|
+
try {
|
|
76
|
+
record(level, args.map((v) => render(v)).join(" "));
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
original.apply(console, args);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
window.addEventListener(
|
|
83
|
+
"error",
|
|
84
|
+
(e) => {
|
|
85
|
+
try {
|
|
86
|
+
const err = e.error;
|
|
87
|
+
const where = e.filename ? ` (${e.filename}:${e.lineno}:${e.colno})` : "";
|
|
88
|
+
record("exception", `${e.message}${where}`, { stack: err?.stack });
|
|
89
|
+
} catch {
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
true
|
|
93
|
+
);
|
|
94
|
+
window.addEventListener("unhandledrejection", (e) => {
|
|
95
|
+
try {
|
|
96
|
+
const r = e.reason;
|
|
97
|
+
record("exception", `Unhandled rejection: ${r?.message ?? render(e.reason)}`, { stack: r?.stack });
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
const netStart = (via, method, url) => {
|
|
102
|
+
const entry = { seq: ++state.seq, ts: Date.now(), via, method, url };
|
|
103
|
+
push(state.network, entry, MAX_NETWORK_ENTRIES);
|
|
104
|
+
return entry;
|
|
105
|
+
};
|
|
106
|
+
const absolute = (url) => {
|
|
107
|
+
try {
|
|
108
|
+
return new URL(url, location.href).href;
|
|
109
|
+
} catch {
|
|
110
|
+
return url;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const originalFetch = w.fetch;
|
|
114
|
+
if (typeof originalFetch === "function") {
|
|
115
|
+
w.fetch = function patchedFetch(input, init) {
|
|
116
|
+
let entry = null;
|
|
117
|
+
let began = 0;
|
|
118
|
+
try {
|
|
119
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url ?? String(input);
|
|
120
|
+
const method = (init?.method ?? input?.method ?? "GET").toUpperCase();
|
|
121
|
+
began = Date.now();
|
|
122
|
+
entry = netStart("fetch", method, clamp(absolute(url)));
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
return originalFetch.call(w, input, init).then(
|
|
126
|
+
(res) => {
|
|
127
|
+
if (entry) {
|
|
128
|
+
entry.status = res.status;
|
|
129
|
+
entry.ok = res.ok;
|
|
130
|
+
entry.durationMs = Date.now() - began;
|
|
131
|
+
}
|
|
132
|
+
return res;
|
|
133
|
+
},
|
|
134
|
+
(err) => {
|
|
135
|
+
if (entry) {
|
|
136
|
+
entry.error = clamp(err instanceof Error ? err.message : String(err));
|
|
137
|
+
entry.durationMs = Date.now() - began;
|
|
138
|
+
}
|
|
139
|
+
throw err;
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const XHR = w.XMLHttpRequest;
|
|
145
|
+
if (typeof XHR === "function") {
|
|
146
|
+
const open = XHR.prototype.open;
|
|
147
|
+
const send = XHR.prototype.send;
|
|
148
|
+
const MARK = "__chromeMcpEntry";
|
|
149
|
+
XHR.prototype.open = function patchedOpen(method, url, ...rest) {
|
|
150
|
+
try {
|
|
151
|
+
this[MARK] = {
|
|
152
|
+
method: String(method ?? "GET").toUpperCase(),
|
|
153
|
+
url: absolute(String(url))
|
|
154
|
+
};
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
return open.call(this, method, url, ...rest);
|
|
158
|
+
};
|
|
159
|
+
XHR.prototype.send = function patchedSend(body) {
|
|
160
|
+
let entry = null;
|
|
161
|
+
const began = Date.now();
|
|
162
|
+
try {
|
|
163
|
+
const meta = this[MARK];
|
|
164
|
+
if (meta) entry = netStart("xhr", meta.method, clamp(meta.url));
|
|
165
|
+
this.addEventListener("loadend", () => {
|
|
166
|
+
if (!entry) return;
|
|
167
|
+
entry.durationMs = Date.now() - began;
|
|
168
|
+
if (this.status === 0) entry.error = "network error or aborted";
|
|
169
|
+
else {
|
|
170
|
+
entry.status = this.status;
|
|
171
|
+
entry.ok = this.status >= 200 && this.status < 400;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
} catch {
|
|
175
|
+
}
|
|
176
|
+
return send.call(this, body);
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const noteDialog = (kind, message, answered) => {
|
|
180
|
+
push(
|
|
181
|
+
state.dialogs,
|
|
182
|
+
{ seq: ++state.seq, ts: Date.now(), kind, message: clamp(message), answered },
|
|
183
|
+
MAX_DIALOG_ENTRIES
|
|
184
|
+
);
|
|
185
|
+
};
|
|
186
|
+
w.alert = function patchedAlert(message) {
|
|
187
|
+
noteDialog("alert", message === void 0 ? "" : String(message), "dismissed");
|
|
188
|
+
};
|
|
189
|
+
w.confirm = function patchedConfirm(message) {
|
|
190
|
+
const answer = state.dialogPolicy === "accept";
|
|
191
|
+
noteDialog("confirm", message === void 0 ? "" : String(message), String(answer));
|
|
192
|
+
return answer;
|
|
193
|
+
};
|
|
194
|
+
w.prompt = function patchedPrompt(message, fallback) {
|
|
195
|
+
const answer = state.dialogPolicy === "accept" ? state.promptText || String(fallback ?? "") : null;
|
|
196
|
+
noteDialog("prompt", message === void 0 ? "" : String(message), answer === null ? "null" : answer);
|
|
197
|
+
return answer;
|
|
198
|
+
};
|
|
199
|
+
window.addEventListener(
|
|
200
|
+
"beforeunload",
|
|
201
|
+
(e) => {
|
|
202
|
+
try {
|
|
203
|
+
if (state.dialogPolicy !== "dismiss") return;
|
|
204
|
+
noteDialog("beforeunload", "page asked to confirm leaving", "suppressed");
|
|
205
|
+
e.stopImmediatePropagation();
|
|
206
|
+
delete e.returnValue;
|
|
207
|
+
} catch {
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
true
|
|
211
|
+
);
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
})();
|
|
215
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mehmoodqureshi/chrome-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Drive your real Chrome browser over MCP — real logins, real cookies. A stdio MCP server (CLI) plus an MV3 extension, driving Chrome via chrome.scripting/chrome.tabs. Multi-tab batch automation, accessibility snapshots, deny-all security by default.",
|
|
5
5
|
"author": "Mehmood Ur Rehman Qureshi",
|
|
6
6
|
"license": "MIT",
|