@debugg-ai/debugg-ai-mcp 6.2.1 → 6.2.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/CHANGELOG.md CHANGED
@@ -7,9 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [6.2.0] — 2026-11-13
10
+ - Local browser runs are headless by default (check_app_in_browser, probe_page, profile path). `headless: false` is opt-in and now launches minimized and off-screen without taking focus.
11
11
 
12
- > Local click-through needs a backend with local executors enabled (platform flag `EXECUTORS_LOCAL_ENABLED`) and `DEBUGGAI_LOCAL_BROWSER=1` on the MCP server. Without both, nothing changes for existing calls.
12
+ ### Changed — local browser is the free default; cloud browsers are the paid option
13
+ - `check_app_in_browser`: leave `browser` unset and it uses your own Chrome when one is installed (stdio server), else the cloud browser; `browser: "local"` or `"remote"` still force one. `DEBUGGAI_LOCAL_BROWSER` is gone: asking for `browser: "local"` is the consent, and the allowlist, sealed credentials and MCP-owned profile are unchanged. It is refused on the hosted HTTP server (no machine of yours). On a backend without local executors an unset `browser` falls back to the cloud browser.
14
+ - A plan without cloud browsers gets a structured `PLAN_REQUIRED` error (HTTP 402) with the message, upgrade link, docs link and how to run in your own Chrome, shown as-is and never retried or silently switched. `probe_page` keeps `browser` defaulting to remote.
15
+
16
+ ## [6.2.1] — 2026-11-13
17
+
18
+ > 6.2.0 was never published; this is its first release. Local click-through needs a backend with local executors enabled (platform flag `EXECUTORS_LOCAL_ENABLED`) and `DEBUGGAI_LOCAL_BROWSER=1` on the MCP server. Without both, nothing changes for existing calls.
13
19
 
14
20
  ### Added — check_app_in_browser on your local Chrome, cloud brain (`browser: "local"`) (epic m7un)
15
21
  - New `browser: "local"` (also `headless`, `useProfile`, `allowedDomains`) runs the check in Chrome on your machine while the cloud brain decides each step. The laptop dials out, so no tunnel is needed for localhost or private apps. Opt-in: set `DEBUGGAI_LOCAL_BROWSER=1` in the MCP server environment; stdio only (not the remote HTTP/OAuth server). The platform side is flag-gated, so it needs a backend that has local executors enabled.
package/README.md CHANGED
@@ -23,6 +23,18 @@ clear error on the first localhost-URL call, not a silent hang. Public-URL calls
23
23
  non-browser tool, and `test_suite {action:"run"}` (which uses its own dedicated tunnel and
24
24
  bypasses Caddy entirely) don't need it either way.
25
25
 
26
+ ## Local Chrome click-through (`browser: "local"`) — free on every plan
27
+
28
+ `check_app_in_browser` can run the check in Chrome on your own machine while the DebuggAI cloud brain decides each step (no tunnel needed, works for private and localhost apps):
29
+
30
+ ```
31
+ check_app_in_browser { "url": "http://localhost:4040", "description": "Sign in and add a task", "browser": "local", "username": "me@x.com", "password": "..." }
32
+ ```
33
+
34
+ - **Free on every plan, and the default when Chrome is installed.** Leave `browser` unset and `check_app_in_browser` uses your own Chrome if it finds one (stdio server); otherwise it uses a cloud browser. Passing `browser: "local"` is all the consent it needs: there is no environment variable to set. Cloud browsers through the MCP (`browser: "remote"`) are included in Professional and above; a plan without them gets `PLAN_REQUIRED` (message, upgrade link, docs link, and how to use your own Chrome instead), never a silent switch. Server-run tests are unchanged.
35
+ - Local runs need a backend with local executors enabled; an unset `browser` quietly uses the cloud browser on a backend without them, while `browser: "local"` says so. Not available on the hosted HTTP/OAuth MCP server (no machine of yours to open Chrome on).
36
+ - Every navigation hop must be on the allowlist (the target host plus `allowedDomains`). Passwords you pass stay on your machine: the cloud sees `{{cred:label}}` and the MCP fills the real value locally. Screenshots go to the cloud by default; `DEBUGGAI_LOCAL_SCREENSHOTS=local_only` keeps them local. `useProfile` keeps persistent cookies between runs, not session cookies.
37
+
26
38
  ## Testing a local app (no Tailscale, no port exposure)
27
39
 
28
40
  Start your app on localhost, then pass the `http://localhost:<port>` URL straight to the tool. The MCP
@@ -49,7 +61,7 @@ Verified 2026-11-10: servers bound to `127.0.0.1` only (IPv4 Caddy gateways on :
49
61
 
50
62
  ### Local Chrome capture (opt-in)
51
63
 
52
- `probe_page` accepts `browser: "local"` to capture with Chrome on your own machine: no tunnel, no cloud browser, nothing uploaded (screenshots also saved under `~/.debugg-ai/captures`, mode 0600). The server owner must set `DEBUGGAI_LOCAL_BROWSER=1`; Chrome is found automatically or via `DEBUGGAI_CHROME_PATH`; `playwright-core` is an optional dependency. `useProfile: true` uses an MCP-owned persistent profile (`~/.debugg-ai/chrome-profile`, never your everyday Chrome profile): sign in once with `headless: false, loginWindowMs: 120000`, later local probes are logged in. Profile sessions require `allowedDomains` (or the owner's `DEBUGGAI_PROFILE_ALLOWED_DOMAINS`, which overrides it); top-level navigations off the list, redirect hops included, are blocked.
64
+ `probe_page` accepts `browser: "local"` to capture with Chrome on your own machine: no tunnel, no cloud browser, nothing uploaded (screenshots also saved under `~/.debugg-ai/captures`, mode 0600). stdio server only; Chrome is found automatically or via `DEBUGGAI_CHROME_PATH`; `playwright-core` is an optional dependency. Local runs are headless by default and never open a visible window; `headless: false` is opt-in and launches minimized and off-screen without taking focus. `useProfile: true` uses an MCP-owned persistent profile (`~/.debugg-ai/chrome-profile`, never your everyday Chrome profile): sign in once with `headless: false, loginWindowMs: 120000`, later local probes are logged in. Profile sessions require `allowedDomains` (or the owner's `DEBUGGAI_PROFILE_ALLOWED_DOMAINS`, which overrides it); top-level navigations off the list, redirect hops included, are blocked.
53
65
 
54
66
  Get an API key at [debugg.ai](https://debugg.ai), then add to your MCP client config:
55
67
 
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { config } from '../config/index.js';
9
9
  import { logger } from '../utils/logger.js';
10
- import { localBrowserEnabled, LocalBrowserError, resolveAllowedDomains } from '../services/localBrowser.js';
10
+ import { localBrowserEnabled, LocalBrowserError, LOCAL_UNAVAILABLE_MESSAGE, findChrome, resolveAllowedDomains } from '../services/localBrowser.js';
11
11
  import { OpRunner } from '../services/localExecutor/opRunner.js';
12
12
  import { runExecutorSession } from '../services/localExecutor/session.js';
13
13
  import { HttpExecutorTransport } from '../services/localExecutor/httpTransport.js';
@@ -31,6 +31,53 @@ export function localizeCredentials(input) {
31
31
  }
32
32
  return { input: out, secrets };
33
33
  }
34
+ /**
35
+ * Local runs are a paid feature. The backend answers a plan that does not include them with
36
+ * HTTP 402 (or a 403 whose code names the plan/entitlement) and a structured body; show its
37
+ * message, upgrade link and docs link as they are and never retry. Returns undefined for any
38
+ * other error.
39
+ */
40
+ export function planRequiredFromError(err, mode = 'local') {
41
+ const e = err;
42
+ const status = e?.statusCode ?? e?.response?.status;
43
+ const raw = (e?.responseData ?? e?.response?.data);
44
+ const body = (raw && typeof raw.error === 'object' && raw.error ? raw.error : raw);
45
+ const pick = (...keys) => { for (const k of keys) {
46
+ const v = body?.[k];
47
+ if (typeof v === 'string' && v)
48
+ return v;
49
+ } return undefined; };
50
+ const code = pick('code', 'error_code', 'errorCode');
51
+ const planish = !!code && /plan|entitle|upgrade|subscription|not_included/i.test(code);
52
+ if (status !== 402 && !(status === 403 && planish))
53
+ return undefined;
54
+ const feature = pick('feature');
55
+ const message = pick('error', 'message', 'detail') ?? (mode === 'remote' ? 'Cloud browsers are not included in your plan.' : 'This run is not included in your plan.');
56
+ const upgradeUrl = pick('upgrade_url', 'upgradeUrl');
57
+ const docsUrl = pick('docs_url', 'docsUrl');
58
+ const required = body?.required_plans ?? body?.requiredPlans;
59
+ // A plan without cloud browsers can still run in the caller's own Chrome, for free. Say how; never fall back silently.
60
+ const alternative = mode === 'remote' ? 'Run it free in your own Chrome: install Chrome and call with browser: "local".' : undefined;
61
+ const hasHint = /browser:\s*["']?local/i.test(message);
62
+ const text = [message, alternative && !hasHint ? alternative : undefined, upgradeUrl ? `Upgrade: ${upgradeUrl}` : undefined, docsUrl ? `Docs: ${docsUrl}` : undefined].filter(Boolean).join(' ');
63
+ return new LocalBrowserError(text, 'PLAN_REQUIRED', { upgradeUrl, docsUrl, ...(feature ? { feature } : {}), ...(required ? { requiredPlans: required } : {}), ...(alternative ? { alternative } : {}) });
64
+ }
65
+ /**
66
+ * Which browser runs this call. An explicit `browser` always wins. Unset: your own Chrome when it is
67
+ * installed, the server is not the hosted one, or the call asks for something only local can do
68
+ * (useProfile); otherwise the cloud browser.
69
+ */
70
+ export function chooseBrowser(input, env) {
71
+ if (input.browser === 'local')
72
+ return { mode: 'local', auto: false };
73
+ if (input.browser === 'remote')
74
+ return { mode: 'remote', auto: false };
75
+ if (input.useProfile)
76
+ return { mode: 'local', auto: false }; // a profile only exists on this machine
77
+ const hosted = env?.hosted ?? !localBrowserEnabled();
78
+ const chrome = env?.chrome ?? !!findChrome();
79
+ return hosted || !chrome ? { mode: 'remote', auto: false } : { mode: 'local', auto: true };
80
+ }
34
81
  export function localAllowedDomains(input, startUrl) {
35
82
  const startHost = (() => { try {
36
83
  return new URL(startUrl).hostname;
@@ -51,7 +98,7 @@ export function localAllowedDomains(input, startUrl) {
51
98
  export async function prepareLocalRun(args) {
52
99
  const { client, input, startUrl, secrets, signal } = args;
53
100
  if (!localBrowserEnabled()) {
54
- throw new LocalBrowserError('Local browser mode is off. Set DEBUGGAI_LOCAL_BROWSER=1 in the MCP server environment to allow it.', 'LOCAL_BROWSER_DISABLED');
101
+ throw new LocalBrowserError(LOCAL_UNAVAILABLE_MESSAGE, 'LOCAL_BROWSER_DISABLED');
55
102
  }
56
103
  const allowed = localAllowedDomains(input, startUrl);
57
104
  if (allowed.length === 0) {
@@ -83,7 +130,10 @@ export async function prepareLocalRun(args) {
83
130
  }
84
131
  catch (err) {
85
132
  (await runnerP.catch(() => null))?.close();
86
- const status = err?.response?.status;
133
+ const planErr = planRequiredFromError(err);
134
+ if (planErr)
135
+ throw planErr;
136
+ const status = err?.response?.status ?? err?.statusCode;
87
137
  if (status === 404) {
88
138
  throw new LocalBrowserError('The DebuggAI backend does not offer local browser execution yet (executor API not enabled for this account).', 'LOCAL_EXECUTOR_UNAVAILABLE');
89
139
  }
@@ -291,6 +291,7 @@ export async function probePageHandler(input, context, rawProgressCallback) {
291
291
  const firstTargetUrl = targetContexts[group.indices[0]]?.targetUrl ?? groupTargets[0].url;
292
292
  const contextData = {
293
293
  targetUrl: firstTargetUrl,
294
+ browserChoice: 'remote', // cloud-browser run: lets the backend apply the plan gate
294
295
  targets: group.indices.map(i => ({
295
296
  url: targetContexts[i].targetUrl ?? input.targets[i].url,
296
297
  // Send null (not undefined) for optional fields so the field exists
@@ -23,7 +23,7 @@ import { extractLocalhostPort, rewriteLocalhostInText } from '../utils/urlParser
23
23
  import { getCachedTemplateUuid, getCachedProjectUuid, invalidateTemplateCache, invalidateProjectCache, } from '../utils/handlerCaches.js';
24
24
  import { isTransientWorkflowError, transientReasonTag } from '../utils/transientErrors.js';
25
25
  import { Telemetry, TelemetryEvents } from '../utils/telemetry.js';
26
- import { prepareLocalRun, localizeCredentials } from './localExecutorBridge.js';
26
+ import { prepareLocalRun, localizeCredentials, planRequiredFromError, chooseBrowser } from './localExecutorBridge.js';
27
27
  import { LocalBrowserError } from '../services/localBrowser.js';
28
28
  import { projectRequiredMessage, projectLookupFailedPayload } from '../utils/projectRequired.js';
29
29
  const logger = new Logger({ module: 'testPageChangesHandler' });
@@ -114,6 +114,7 @@ export async function testPageChangesHandler(input, context, progressCallback) {
114
114
  }
115
115
  async function testPageChangesHandlerInner(input, context, rawProgressCallback) {
116
116
  const startTime = Date.now();
117
+ const originalInput = input; // before local credential placeholders are applied
117
118
  logger.toolStart('check_app_in_browser', input);
118
119
  // Bead 0bq: wrap the progress callback in a circuit-breaker so a single
119
120
  // client-side rejection of a stale progressToken (which would normally
@@ -139,7 +140,8 @@ async function testPageChangesHandlerInner(input, context, rawProgressCallback)
139
140
  const client = new DebuggAIServerClient(config.api.key);
140
141
  await client.init();
141
142
  // browser:'local': Chrome on this machine does the clicking (no tunnel); passwords stay local.
142
- const localMode = input.browser === 'local';
143
+ const choice = chooseBrowser(input);
144
+ const localMode = choice.mode === 'local';
143
145
  let localSecrets = {};
144
146
  if (localMode) {
145
147
  const l = localizeCredentials(input);
@@ -412,6 +414,7 @@ async function testPageChangesHandlerInner(input, context, rawProgressCallback)
412
414
  contextData.projectId = projectUuid;
413
415
  }
414
416
  contextData.headless = true; // D7: the MCP always runs headless — no opt-out.
417
+ contextData.browserChoice = localMode ? 'local' : 'remote'; // lets the backend apply the cloud-browser plan gate
415
418
  if (localMode) {
416
419
  localRun = await prepareLocalRun({ client, input, startUrl: originalUrl, secrets: localSecrets, signal: abortController.signal, projectId: projectUuid || undefined });
417
420
  contextData.executorSessionId = localRun.sessionId;
@@ -953,9 +956,16 @@ async function testPageChangesHandlerInner(input, context, rawProgressCallback)
953
956
  return tunnelFault ? { content, isError: true } : { content };
954
957
  }
955
958
  catch (error) {
956
- if (error instanceof LocalBrowserError) {
957
- const payload = { error: error.code, message: error.message };
958
- logger.warn(`check_app_in_browser (local): ${error.code}: ${error.message}`);
959
+ // Browser was not chosen and this backend has no local executors: use the cloud browser instead.
960
+ if (choice.auto && error instanceof LocalBrowserError && error.code === 'LOCAL_EXECUTOR_UNAVAILABLE') {
961
+ logger.info('check_app_in_browser: local executors are not available on this backend; using the cloud browser');
962
+ return testPageChangesHandlerInner({ ...originalInput, browser: 'remote' }, context, rawProgressCallback);
963
+ }
964
+ const planErr = planRequiredFromError(error, localMode ? 'local' : 'remote');
965
+ if (error instanceof LocalBrowserError || planErr) {
966
+ const lbe = (planErr ?? error);
967
+ const payload = { error: lbe.code, message: lbe.message, ...(lbe.details ?? {}) };
968
+ logger.warn(`check_app_in_browser (local): ${lbe.code}: ${lbe.message}`);
959
969
  return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }], isError: true };
960
970
  }
961
971
  const duration = Date.now() - startTime;
@@ -6,7 +6,8 @@
6
6
  * Everything stays local: screenshots are returned inline and also written under
7
7
  * ~/.debugg-ai/captures (0600).
8
8
  *
9
- * Opt-in at the server: DEBUGGAI_LOCAL_BROWSER=1. With `useProfile`, the browser
9
+ * No server opt-in: asking for `browser: "local"` in the call is the consent (the allowlist stays
10
+ * required). It is unavailable on the hosted HTTP server, which has no machine of yours. With `useProfile`, the browser
10
11
  * uses an MCP-owned persistent profile (~/.debugg-ai/chrome-profile, override with
11
12
  * DEBUGGAI_CHROME_PROFILE_DIR) the user logs into once via `loginToLocalProfile`;
12
13
  * the user's everyday Chrome profile is never touched.
@@ -17,9 +18,11 @@ import path from 'node:path';
17
18
  import { installNavigationGuard, isLoopbackHost } from './navigationGuard.js';
18
19
  export class LocalBrowserError extends Error {
19
20
  code;
20
- constructor(message, code) {
21
+ details;
22
+ constructor(message, code, details) {
21
23
  super(message);
22
24
  this.code = code;
25
+ this.details = details;
23
26
  this.name = 'LocalBrowserError';
24
27
  }
25
28
  }
@@ -40,9 +43,11 @@ export function resolveAllowedDomains(perCall, env = process.env) {
40
43
  const owner = (env.DEBUGGAI_PROFILE_ALLOWED_DOMAINS ?? '').split(',').map((s) => s.trim()).filter(Boolean);
41
44
  return owner.length > 0 ? owner : (perCall ?? []).filter(Boolean);
42
45
  }
46
+ /** Local Chrome needs a machine of the caller's: it is available on stdio, never on the hosted HTTP server. */
43
47
  export function localBrowserEnabled(env = process.env) {
44
- return env.DEBUGGAI_LOCAL_BROWSER === '1' || env.DEBUGGAI_LOCAL_BROWSER === 'true';
48
+ return (env.DEBUGGAI_MCP_TRANSPORT || 'stdio').toLowerCase() !== 'http';
45
49
  }
50
+ export const LOCAL_UNAVAILABLE_MESSAGE = 'Local browser runs are not available on the hosted (HTTP) MCP server: there is no machine of yours to open Chrome on. Use browser "remote", or run the MCP on your own machine (stdio).';
46
51
  export function debuggHome(env = process.env) {
47
52
  return env.DEBUGGAI_HOME || path.join(os.homedir(), '.debugg-ai');
48
53
  }
@@ -74,10 +79,12 @@ async function loadPlaywright() {
74
79
  export function ensurePrivateDir(dir) {
75
80
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
76
81
  }
82
+ /** Headed runs are opt-in; even then keep the window out of the way: off-screen, minimized, no focus grab. */
83
+ export const HEADED_QUIET_ARGS = ['--start-minimized', '--window-position=-32000,-32000', '--no-first-run', '--no-default-browser-check'];
77
84
  export async function launch(opts) {
78
85
  const pw = await loadPlaywright();
79
86
  const executablePath = findChrome();
80
- const base = { headless: opts.headless, ...(executablePath ? { executablePath } : { channel: 'chrome' }) };
87
+ const base = { headless: opts.headless, ...(opts.headless ? {} : { args: HEADED_QUIET_ARGS }), ...(executablePath ? { executablePath } : { channel: 'chrome' }) };
81
88
  try {
82
89
  if (opts.useProfile) {
83
90
  const dir = profileDir();
@@ -120,7 +127,7 @@ function summarize(entries) {
120
127
  }
121
128
  export async function localCapture(o) {
122
129
  if (!localBrowserEnabled()) {
123
- throw new LocalBrowserError('Local browser mode is off. Set DEBUGGAI_LOCAL_BROWSER=1 in the MCP server environment to allow it.', 'LOCAL_BROWSER_DISABLED');
130
+ throw new LocalBrowserError(LOCAL_UNAVAILABLE_MESSAGE, 'LOCAL_BROWSER_DISABLED');
124
131
  }
125
132
  const allowed = o.useProfile ? resolveAllowedDomains(o.allowedDomains) : [];
126
133
  if (o.useProfile && allowed.length === 0) {
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import fs from 'node:fs';
8
8
  import path from 'node:path';
9
- import { launch, localBrowserEnabled, LocalBrowserError, hostAllowed, resolveAllowedDomains, debuggHome, ensurePrivateDir } from '../localBrowser.js';
9
+ import { launch, localBrowserEnabled, LocalBrowserError, LOCAL_UNAVAILABLE_MESSAGE, hostAllowed, resolveAllowedDomains, debuggHome, ensurePrivateDir } from '../localBrowser.js';
10
10
  import { installNavigationGuard, isLoopbackHost } from '../navigationGuard.js';
11
11
  import { OPS } from './ops.js';
12
12
  const SECRET_RE = /\{\{cred:([^}]+)\}\}/g;
@@ -45,7 +45,7 @@ export class OpRunner {
45
45
  static supportedOps() { return OPS; }
46
46
  static async open(opts) {
47
47
  if (!localBrowserEnabled()) {
48
- throw new LocalBrowserError('Local browser mode is off. Set DEBUGGAI_LOCAL_BROWSER=1 in the MCP server environment to allow it.', 'LOCAL_BROWSER_DISABLED');
48
+ throw new LocalBrowserError(LOCAL_UNAVAILABLE_MESSAGE, 'LOCAL_BROWSER_DISABLED');
49
49
  }
50
50
  const allowed = opts.useProfile ? resolveAllowedDomains(opts.allowedDomains) : opts.allowedDomains;
51
51
  if (allowed.length === 0) {
@@ -84,7 +84,7 @@ export function buildProbePageTool() {
84
84
  browser: {
85
85
  type: 'string',
86
86
  enum: ['remote', 'local'],
87
- description: "Default 'remote' (cloud browser; localhost is tunneled). 'local' runs Chrome on THIS machine against the URL directly: no tunnel, nothing uploaded, screenshot also saved under ~/.debugg-ai/captures. Works only when the MCP server runs on your machine AND its owner set DEBUGGAI_LOCAL_BROWSER=1. waitForSelector/waitForLoadState/timeoutMs apply; the local mode returns screenshot, console errors, network summary, title and (optionally) HTML.",
87
+ description: "Default 'remote' (cloud browser; localhost is tunneled). 'local' runs Chrome on THIS machine against the URL directly: no tunnel, nothing uploaded, screenshot also saved under ~/.debugg-ai/captures. Works only when the MCP server runs on your machine (stdio). waitForSelector/waitForLoadState/timeoutMs apply; the local mode returns screenshot, console errors, network summary, title and (optionally) HTML.",
88
88
  },
89
89
  headless: {
90
90
  type: 'boolean',
@@ -109,7 +109,7 @@ export function buildTestPageChangesTool(ctx) {
109
109
  browser: {
110
110
  type: "string",
111
111
  enum: ["remote", "local"],
112
- description: "Default 'remote' (cloud browser; localhost is tunneled). 'local' runs the clicking in Chrome on THIS machine — no tunnel, no *.localhost limits, much faster — while the cloud agent still decides each step. Needs DEBUGGAI_LOCAL_BROWSER=1 on the MCP server and the backend's local executor enabled. Passwords passed in this call stay on this machine. Page screenshots and text are still sent to the cloud agent. Not available over the remote HTTP MCP."
112
+ description: "Unset: Chrome on THIS machine when it is installed, otherwise the cloud browser (a plan with cloud browsers; localhost is tunneled). 'local' runs the clicking in your own Chrome — free on every plan, no tunnel, no *.localhost limits, much faster — while the cloud agent still decides each step. 'remote' forces the cloud browser (Professional and above). Passwords passed in this call stay on this machine. Page screenshots and text are still sent to the cloud agent. Not available over the remote HTTP MCP."
113
113
  },
114
114
  headless: { type: "boolean", description: "browser:'local' only. Default true; false shows the Chrome window." },
115
115
  useProfile: { type: "boolean", description: "browser:'local' only. Use the MCP-owned logged-in Chrome profile (never your everyday Chrome). Requires allowedDomains." },
@@ -65,8 +65,8 @@ export const TestPageChangesInputSchema = z.object({
65
65
  // you are checking, or when the app's only route between personas is a logout.
66
66
  freshSession: z.boolean().optional(),
67
67
  // 'local' runs the clicking in Chrome on THIS machine (no tunnel); the cloud agent still decides each step.
68
- // Needs DEBUGGAI_LOCAL_BROWSER=1 on the MCP server.
69
- browser: z.enum(['remote', 'local']).default('remote'),
68
+ // Unset: your own Chrome when it is installed (stdio server), otherwise the cloud browser.
69
+ browser: z.enum(['remote', 'local']).optional(),
70
70
  headless: z.boolean().optional(),
71
71
  useProfile: z.boolean().optional(),
72
72
  allowedDomains: z.array(z.string().min(1)).max(32).optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugg-ai/debugg-ai-mcp",
3
- "version": "6.2.1",
3
+ "version": "6.2.2",
4
4
  "description": "Zero-Config, Fully AI-Managed End-to-End Testing for all code gen platforms.",
5
5
  "type": "module",
6
6
  "caddy": "2.11.3",