cbrowser 19.3.8 → 19.4.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/dist/analysis/accessibility-empathy.d.ts.map +1 -1
- package/dist/analysis/accessibility-empathy.js +10 -1
- package/dist/analysis/accessibility-empathy.js.map +1 -1
- package/dist/analysis/agent-ready-audit.d.ts +9 -0
- package/dist/analysis/agent-ready-audit.d.ts.map +1 -1
- package/dist/analysis/agent-ready-audit.js +79 -6
- package/dist/analysis/agent-ready-audit.js.map +1 -1
- package/dist/analysis/bug-hunter.d.ts.map +1 -1
- package/dist/analysis/bug-hunter.js +27 -10
- package/dist/analysis/bug-hunter.js.map +1 -1
- package/dist/analysis/chaos-testing.d.ts.map +1 -1
- package/dist/analysis/chaos-testing.js +12 -3
- package/dist/analysis/chaos-testing.js.map +1 -1
- package/dist/analysis/competitive-benchmark.d.ts +50 -7
- package/dist/analysis/competitive-benchmark.d.ts.map +1 -1
- package/dist/analysis/competitive-benchmark.js +226 -106
- package/dist/analysis/competitive-benchmark.js.map +1 -1
- package/dist/analysis/page-understanding.d.ts +2 -0
- package/dist/analysis/page-understanding.d.ts.map +1 -1
- package/dist/analysis/page-understanding.js +104 -17
- package/dist/analysis/page-understanding.js.map +1 -1
- package/dist/artifact-store.d.ts +24 -2
- package/dist/artifact-store.d.ts.map +1 -1
- package/dist/artifact-store.js +34 -4
- package/dist/artifact-store.js.map +1 -1
- package/dist/browser/session-manager.d.ts.map +1 -1
- package/dist/browser/session-manager.js +3 -0
- package/dist/browser/session-manager.js.map +1 -1
- package/dist/browser/site-profile-manager.d.ts +17 -1
- package/dist/browser/site-profile-manager.d.ts.map +1 -1
- package/dist/browser/site-profile-manager.js +49 -12
- package/dist/browser/site-profile-manager.js.map +1 -1
- package/dist/browser.d.ts +58 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +377 -50
- package/dist/browser.js.map +1 -1
- package/dist/charge-scope.d.ts.map +1 -1
- package/dist/charge-scope.js +6 -4
- package/dist/charge-scope.js.map +1 -1
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -2
- package/dist/config.js.map +1 -1
- package/dist/mcp-server-remote.d.ts +26 -1
- package/dist/mcp-server-remote.d.ts.map +1 -1
- package/dist/mcp-server-remote.js +159 -89
- package/dist/mcp-server-remote.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +4 -0
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-tools/base/analysis-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/analysis-tools.js +5 -0
- package/dist/mcp-tools/base/analysis-tools.js.map +1 -1
- package/dist/mcp-tools/base/audit-tools.d.ts +14 -0
- package/dist/mcp-tools/base/audit-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/audit-tools.js +17 -8
- package/dist/mcp-tools/base/audit-tools.js.map +1 -1
- package/dist/mcp-tools/base/browser-state-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/browser-state-tools.js +18 -27
- package/dist/mcp-tools/base/browser-state-tools.js.map +1 -1
- package/dist/mcp-tools/base/capture-tools.d.ts +18 -0
- package/dist/mcp-tools/base/capture-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/capture-tools.js +109 -55
- package/dist/mcp-tools/base/capture-tools.js.map +1 -1
- package/dist/mcp-tools/base/cognitive-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/cognitive-tools.js +43 -16
- package/dist/mcp-tools/base/cognitive-tools.js.map +1 -1
- package/dist/mcp-tools/base/marketing-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/marketing-tools.js +8 -4
- package/dist/mcp-tools/base/marketing-tools.js.map +1 -1
- package/dist/mcp-tools/base/persona-comparison-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/persona-comparison-tools.js +14 -3
- package/dist/mcp-tools/base/persona-comparison-tools.js.map +1 -1
- package/dist/mcp-tools/base/remediation-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/remediation-tools.js +10 -2
- package/dist/mcp-tools/base/remediation-tools.js.map +1 -1
- package/dist/mcp-tools/base/session-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/session-tools.js +2 -0
- package/dist/mcp-tools/base/session-tools.js.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.d.ts +13 -0
- package/dist/mcp-tools/base/visual-testing-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.js +45 -16
- package/dist/mcp-tools/base/visual-testing-tools.js.map +1 -1
- package/dist/mcp-tools/persona-creation-tools.d.ts.map +1 -1
- package/dist/mcp-tools/persona-creation-tools.js +59 -13
- package/dist/mcp-tools/persona-creation-tools.js.map +1 -1
- package/dist/mcp-tools/persona-lifecycle-tools.d.ts.map +1 -1
- package/dist/mcp-tools/persona-lifecycle-tools.js +4 -2
- package/dist/mcp-tools/persona-lifecycle-tools.js.map +1 -1
- package/dist/mcp-tools/tier-gate.d.ts +4 -4
- package/dist/mcp-tools/tier-gate.d.ts.map +1 -1
- package/dist/mcp-tools/tier-gate.js +41 -19
- package/dist/mcp-tools/tier-gate.js.map +1 -1
- package/dist/mcp-tools/tool-categories.d.ts +11 -0
- package/dist/mcp-tools/tool-categories.d.ts.map +1 -1
- package/dist/mcp-tools/tool-categories.js +27 -1
- package/dist/mcp-tools/tool-categories.js.map +1 -1
- package/dist/mcp-tools/ui-resources.d.ts.map +1 -1
- package/dist/mcp-tools/ui-resources.js +32 -10
- package/dist/mcp-tools/ui-resources.js.map +1 -1
- package/dist/personas.d.ts +14 -0
- package/dist/personas.d.ts.map +1 -1
- package/dist/personas.js +26 -0
- package/dist/personas.js.map +1 -1
- package/dist/recording/engine.d.ts.map +1 -1
- package/dist/recording/engine.js +2 -1
- package/dist/recording/engine.js.map +1 -1
- package/dist/remediation/patch-generator.d.ts.map +1 -1
- package/dist/remediation/patch-generator.js +7 -1
- package/dist/remediation/patch-generator.js.map +1 -1
- package/dist/remediation/structured-data.d.ts +73 -1
- package/dist/remediation/structured-data.d.ts.map +1 -1
- package/dist/remediation/structured-data.js +239 -63
- package/dist/remediation/structured-data.js.map +1 -1
- package/dist/request-context.d.ts +18 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +56 -0
- package/dist/request-context.js.map +1 -0
- package/dist/site-model/manager.d.ts +14 -1
- package/dist/site-model/manager.d.ts.map +1 -1
- package/dist/site-model/manager.js +24 -1
- package/dist/site-model/manager.js.map +1 -1
- package/dist/types.d.ts +90 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/values/index.d.ts +1 -1
- package/dist/values/index.d.ts.map +1 -1
- package/dist/values/index.js +1 -1
- package/dist/values/index.js.map +1 -1
- package/dist/values/persona-values.d.ts +16 -0
- package/dist/values/persona-values.d.ts.map +1 -1
- package/dist/values/persona-values.js +56 -4
- package/dist/values/persona-values.js.map +1 -1
- package/dist/visual/ab-comparison.d.ts +22 -0
- package/dist/visual/ab-comparison.d.ts.map +1 -1
- package/dist/visual/ab-comparison.js +36 -9
- package/dist/visual/ab-comparison.js.map +1 -1
- package/dist/visual/cognitive-transport.d.ts +15 -1
- package/dist/visual/cognitive-transport.d.ts.map +1 -1
- package/dist/visual/cognitive-transport.js +42 -12
- package/dist/visual/cognitive-transport.js.map +1 -1
- package/dist/visual/cross-browser.d.ts +8 -0
- package/dist/visual/cross-browser.d.ts.map +1 -1
- package/dist/visual/cross-browser.js +8 -4
- package/dist/visual/cross-browser.js.map +1 -1
- package/dist/visual/distance-metrics.d.ts +11 -2
- package/dist/visual/distance-metrics.d.ts.map +1 -1
- package/dist/visual/distance-metrics.js +44 -5
- package/dist/visual/distance-metrics.js.map +1 -1
- package/dist/visual/regression.d.ts +61 -0
- package/dist/visual/regression.d.ts.map +1 -1
- package/dist/visual/regression.js +123 -50
- package/dist/visual/regression.js.map +1 -1
- package/dist/visual/responsive.d.ts +8 -0
- package/dist/visual/responsive.d.ts.map +1 -1
- package/dist/visual/responsive.js +95 -17
- package/dist/visual/responsive.js.map +1 -1
- package/package.json +1 -1
|
@@ -40,16 +40,18 @@ import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
|
40
40
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
41
41
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
42
42
|
import { CBrowser } from "./browser.js";
|
|
43
|
-
import { ensureDirectories } from "./config.js";
|
|
43
|
+
import { ensureDirectories, getDataDir } from "./config.js";
|
|
44
44
|
// Version from package.json - single source of truth
|
|
45
45
|
import { VERSION } from "./version.js";
|
|
46
46
|
// Modular MCP tools (v17.5.0)
|
|
47
47
|
// getActiveKeyHash is deliberately no longer imported: billing identity is per-request
|
|
48
|
-
// (see resolveRequestKeyHash). setActiveKeyHash
|
|
49
|
-
//
|
|
48
|
+
// (see resolveRequestKeyHash). setActiveKeyHash, setActiveTier and setSessionApiKey
|
|
49
|
+
// write the calling request's own identity context (request-context.ts), which every
|
|
50
|
+
// HTTP request enters at createServer below. (sec-identity, 2026-10-10)
|
|
50
51
|
import { registerAllPublicTools, setRemoteMode, setHostedMode, setActiveTier as setTierGate, setActiveKeyHash, isToolAccessible, upgradePrompt } from "./mcp-tools/index.js";
|
|
51
52
|
import { withPersonaScope, scopedAccountId } from "./persona-scope.js";
|
|
52
53
|
import { withChargeScope } from "./charge-scope.js";
|
|
54
|
+
import { withRequestIdentity } from "./request-context.js";
|
|
53
55
|
/**
|
|
54
56
|
* keyHash -> accountId, so a request can be scoped to the tenant that owns it.
|
|
55
57
|
*
|
|
@@ -76,6 +78,8 @@ export async function resolveRequestAccountId(req) {
|
|
|
76
78
|
const after = accountCache.get(keyHash);
|
|
77
79
|
return after && after.expiresAt > Date.now() ? after.accountId : null;
|
|
78
80
|
}
|
|
81
|
+
import { TOOL_CATEGORIES, categorizedToolNames } from "./mcp-tools/tool-categories.js";
|
|
82
|
+
import { BUILTIN_PERSONAS, ACCESSIBILITY_PERSONAS, EMOTIONAL_PERSONAS, AGENT_PERSONAS } from "./personas.js";
|
|
79
83
|
// Shared browser instance (single Chromium process)
|
|
80
84
|
let browser = null;
|
|
81
85
|
// Stealth state (enterprise integration)
|
|
@@ -453,6 +457,10 @@ async function getBrowser() {
|
|
|
453
457
|
headless: true,
|
|
454
458
|
persistent: true,
|
|
455
459
|
proxy: proxyConfig,
|
|
460
|
+
// The shared legacy browser belongs to no account, so its files stay
|
|
461
|
+
// in the process data dir even if it is first built inside a scope;
|
|
462
|
+
// the config default is now the caller's directory. (tenant, 2026-10-10)
|
|
463
|
+
dataDir: getDataDir(),
|
|
456
464
|
});
|
|
457
465
|
}
|
|
458
466
|
return browser;
|
|
@@ -639,6 +647,35 @@ const tierCache = new Map();
|
|
|
639
647
|
const MAX_REQUEST_BODY_BYTES = 4 * 1024 * 1024;
|
|
640
648
|
/** Bound the CMS deduct call; an unanswered one used to hang the whole request. */
|
|
641
649
|
const CREDIT_DEDUCT_TIMEOUT_MS = 5_000;
|
|
650
|
+
/**
|
|
651
|
+
* "none" on the enterprise server: enterprise is unlimited use (Alexa, 2026-10-12).
|
|
652
|
+
* The deduct call is still made, because the CMS deduct route is also the domain,
|
|
653
|
+
* tier and URL gate; `charge=none` makes the CMS price the call at 0. Set once at
|
|
654
|
+
* startup from `RemoteMcpServerOptions.chargeCredits`.
|
|
655
|
+
*/
|
|
656
|
+
let creditChargeMode = "charge";
|
|
657
|
+
export function setCreditChargeMode(mode) {
|
|
658
|
+
creditChargeMode = mode;
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Did this deduct response actually take credits? Decides whether the call enters a
|
|
662
|
+
* charge scope (where an isError result is refunded). A 0-credit deduct (charge=none,
|
|
663
|
+
* or a free tool) took nothing, and the CMS refund route credits the catalog price
|
|
664
|
+
* without finding the matching deduct, so refunding it would mint credits. A CMS that
|
|
665
|
+
* does not report `cost` keeps the old behaviour.
|
|
666
|
+
*/
|
|
667
|
+
export function chargedByDeduct(httpOk, data) {
|
|
668
|
+
return httpOk && data.allowed === true && data.cost !== 0;
|
|
669
|
+
}
|
|
670
|
+
/** Query string for POST /api/credits/deduct. */
|
|
671
|
+
export function deductQuery(keyHash, toolName, targetUrl) {
|
|
672
|
+
return new URLSearchParams({
|
|
673
|
+
key_hash: keyHash,
|
|
674
|
+
tool: toolName,
|
|
675
|
+
...(targetUrl ? { target_url: targetUrl } : {}),
|
|
676
|
+
...(creditChargeMode === "none" ? { charge: "none" } : {}),
|
|
677
|
+
}).toString();
|
|
678
|
+
}
|
|
642
679
|
const TIER_CACHE_TTL_MS = 30 * 1000;
|
|
643
680
|
/**
|
|
644
681
|
* Extract the raw API key from an incoming HTTP request.
|
|
@@ -1084,6 +1121,13 @@ function sendUnauthorized(res, message) {
|
|
|
1084
1121
|
}),
|
|
1085
1122
|
}));
|
|
1086
1123
|
}
|
|
1124
|
+
/**
|
|
1125
|
+
* The tier a request is gated at.
|
|
1126
|
+
* Priority: resolved from API key > CBROWSER_TIER env > null (self-hosted, no gating).
|
|
1127
|
+
*/
|
|
1128
|
+
function effectiveTier(resolvedTier) {
|
|
1129
|
+
return resolvedTier ?? process.env.CBROWSER_TIER ?? null;
|
|
1130
|
+
}
|
|
1087
1131
|
/**
|
|
1088
1132
|
* Configure all CBrowser tools on an MCP server instance.
|
|
1089
1133
|
* This is shared between stdio and HTTP transports.
|
|
@@ -1101,8 +1145,7 @@ function configureMcpTools(server, customRegisterTools, sessionId, resolvedTier)
|
|
|
1101
1145
|
? () => getSessionBrowser(sessionId)
|
|
1102
1146
|
: getTokenlessBrowser;
|
|
1103
1147
|
const context = { getBrowser: sessionGetBrowser, getBrowserByToken };
|
|
1104
|
-
|
|
1105
|
-
const tier = resolvedTier ?? process.env.CBROWSER_TIER ?? null;
|
|
1148
|
+
const tier = effectiveTier(resolvedTier);
|
|
1106
1149
|
if (customRegisterTools) {
|
|
1107
1150
|
// Set tier BEFORE custom registration so createGatedServer uses resolved tier
|
|
1108
1151
|
setTierGate(tier);
|
|
@@ -1113,30 +1156,30 @@ function configureMcpTools(server, customRegisterTools, sessionId, resolvedTier)
|
|
|
1113
1156
|
}
|
|
1114
1157
|
}
|
|
1115
1158
|
/**
|
|
1116
|
-
*
|
|
1159
|
+
* Counts this server prints about itself, all derived, never typed.
|
|
1117
1160
|
*
|
|
1118
|
-
*
|
|
1119
|
-
*
|
|
1161
|
+
* Every number below used to be a literal, and they disagreed with each other
|
|
1162
|
+
* and with tools/list: the instructions said "120 tools across 16 categories",
|
|
1163
|
+
* docs://tools-overview said 105 in its description and "120 across 11" in its
|
|
1164
|
+
* body, persona_creation was "(6 tools)" with nine registered, and
|
|
1165
|
+
* docs://personas said 21 while listing 6 of the 10 general personas. Measured
|
|
1166
|
+
* 2026-10-10: tools/list on the enterprise server returned 130 tools, 126 of
|
|
1167
|
+
* them in TOOL_CATEGORIES' 20 categories and 4 in none (B33).
|
|
1120
1168
|
*/
|
|
1121
|
-
function
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
tools: { listChanged: true },
|
|
1136
|
-
prompts: {},
|
|
1137
|
-
resources: {},
|
|
1138
|
-
},
|
|
1139
|
-
instructions: `CBrowser — cognitive browser automation with 120 tools across 16 categories.
|
|
1169
|
+
function catalogCounts() {
|
|
1170
|
+
return { tools: categorizedToolNames().length, categories: TOOL_CATEGORIES.length };
|
|
1171
|
+
}
|
|
1172
|
+
/** One line per specialist category, counted from TOOL_CATEGORIES. */
|
|
1173
|
+
function specialistCategoryLines() {
|
|
1174
|
+
return TOOL_CATEGORIES
|
|
1175
|
+
.filter((cat) => cat.tier > 1)
|
|
1176
|
+
.map((cat) => `• ${cat.name} (${cat.tools.length} tool${cat.tools.length === 1 ? "" : "s"}) — ${cat.tools.slice(0, 5).join(", ")}${cat.tools.length > 5 ? "..." : ""}`)
|
|
1177
|
+
.join("\n");
|
|
1178
|
+
}
|
|
1179
|
+
/** The server instructions sent on initialize. Exported so the counts can be tested. */
|
|
1180
|
+
export function buildServerInstructions() {
|
|
1181
|
+
const { tools, categories } = catalogCounts();
|
|
1182
|
+
return `CBrowser — cognitive browser automation with ${tools} tools across ${categories} categories.
|
|
1140
1183
|
|
|
1141
1184
|
CORE TOOLS (use these first):
|
|
1142
1185
|
• navigate, click, fill, scroll, screenshot, extract — browser control
|
|
@@ -1149,17 +1192,7 @@ CORE TOOLS (use these first):
|
|
|
1149
1192
|
• security_audit — MCP tool injection scanning
|
|
1150
1193
|
|
|
1151
1194
|
SPECIALIST CATEGORIES (use when the task requires them):
|
|
1152
|
-
|
|
1153
|
-
• cognitive_transport (7 tools) — cognitive_distance, cognitive_coverage, cognitive_interpolate, cognitive_load_estimate, compare_personas...
|
|
1154
|
-
• testing (5 tools) — nl_test_inline, nl_test_file, detect_flaky_tests, coverage_map, repair_test
|
|
1155
|
-
• site_knowledge (5 tools) — site_model_status, site_model_query, site_profile_list/delete/status
|
|
1156
|
-
• bug_hunting (3 tools) — hunt_bugs, chaos_test, competitive_benchmark
|
|
1157
|
-
• performance (3 tools) — perf_baseline, perf_regression, list_baselines
|
|
1158
|
-
• sessions (4 tools) — save_session, load_session, list_sessions, delete_session
|
|
1159
|
-
• marketing (4 tools) — marketing_campaign_create/run/report, marketing_personas_list
|
|
1160
|
-
• persona_creation (6 tools) — persona_create_start, questionnaire, from_description
|
|
1161
|
-
• enterprise_security (8 tools) — web_security_scan, stealth_enable/disable/check, cloudflare_detect/wait
|
|
1162
|
-
• enterprise_marketing (6 tools) — marketing_influence_matrix, lever_analysis, funnel_analyze, audience_discover
|
|
1195
|
+
${specialistCategoryLines()}
|
|
1163
1196
|
|
|
1164
1197
|
TIPS:
|
|
1165
1198
|
• Pass _browserToken between tool calls to maintain browser state
|
|
@@ -1178,7 +1211,56 @@ SELF-HELP:
|
|
|
1178
1211
|
• If you are unsure what a tool does, what a score means, which persona to use, or how tools work together — call question_answer with your question
|
|
1179
1212
|
• question_answer searches 290+ pages of CBrowser documentation and returns a grounded answer
|
|
1180
1213
|
• Use it before guessing. Example: question_answer(question: "What is the difference between viewport and full_page scope?")
|
|
1181
|
-
• Use it to explain results to the user. Example: question_answer(question: "What does a CTC of 1.6 with readability bottleneck mean?")
|
|
1214
|
+
• Use it to explain results to the user. Example: question_answer(question: "What does a CTC of 1.6 with readability bottleneck mean?")`;
|
|
1215
|
+
}
|
|
1216
|
+
/** Body of docs://tools-overview, counted from TOOL_CATEGORIES. */
|
|
1217
|
+
export function buildToolsOverviewText() {
|
|
1218
|
+
const { tools, categories } = catalogCounts();
|
|
1219
|
+
const list = TOOL_CATEGORIES.map((cat) => `${cat.name} (${cat.tools.length})`).join(", ");
|
|
1220
|
+
return `CBrowser provides ${tools} MCP tools across ${categories} categories: ${list}. Visit https://cbrowser.ai/docs/Tools-Overview for the complete reference.`;
|
|
1221
|
+
}
|
|
1222
|
+
/** Body of docs://personas, listed from the persona maps themselves. */
|
|
1223
|
+
export function buildPersonasOverviewText() {
|
|
1224
|
+
const groups = [
|
|
1225
|
+
["Cognitive", Object.keys(BUILTIN_PERSONAS)],
|
|
1226
|
+
["Accessibility", Object.keys(ACCESSIBILITY_PERSONAS)],
|
|
1227
|
+
["Emotional", Object.keys(EMOTIONAL_PERSONAS)],
|
|
1228
|
+
["AI agent", Object.keys(AGENT_PERSONAS)],
|
|
1229
|
+
];
|
|
1230
|
+
const total = groups.reduce((n, [, names]) => n + names.length, 0);
|
|
1231
|
+
const body = groups.map(([label, names]) => `${label} (${names.length}): ${names.join(", ")}`).join(". ");
|
|
1232
|
+
return `CBrowser includes ${total} built-in personas: ${body}. Each human persona has 26 cognitive traits; agent personas describe AI agents with agent traits instead. Visit https://cbrowser.ai/docs/Persona-Index for details.`;
|
|
1233
|
+
}
|
|
1234
|
+
/** Number of built-in personas across every map docs://personas lists. */
|
|
1235
|
+
function builtinPersonaCount() {
|
|
1236
|
+
return [BUILTIN_PERSONAS, ACCESSIBILITY_PERSONAS, EMOTIONAL_PERSONAS, AGENT_PERSONAS]
|
|
1237
|
+
.reduce((n, map) => n + Object.keys(map).length, 0);
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Create a configured MCP server instance
|
|
1241
|
+
*
|
|
1242
|
+
* @param customRegisterTools - Optional custom tool registration
|
|
1243
|
+
* @param sessionId - Optional session ID for session isolation
|
|
1244
|
+
*/
|
|
1245
|
+
function createMcpServer(customRegisterTools, sessionId, resolvedTier) {
|
|
1246
|
+
const server = new McpServer({
|
|
1247
|
+
name: "cbrowser",
|
|
1248
|
+
title: "CBrowser — Cognitive Browser Automation",
|
|
1249
|
+
version: VERSION,
|
|
1250
|
+
description: `AI-powered browser automation with cognitive user simulation. ${catalogCounts().tools} MCP tools for navigation, visual testing, accessibility auditing, persona-based testing, site knowledge learning, and security scanning. Simulates how real humans think, struggle, and give up on your site.`,
|
|
1251
|
+
websiteUrl: "https://cbrowser.ai",
|
|
1252
|
+
icons: [
|
|
1253
|
+
{ src: "https://cbrowser.ai/icon-192.png", sizes: ["192x192"] },
|
|
1254
|
+
{ src: "https://cbrowser.ai/icon-512.png", sizes: ["512x512"] },
|
|
1255
|
+
{ src: "https://cbrowser.ai/logo.svg", sizes: ["1080x1080"] },
|
|
1256
|
+
],
|
|
1257
|
+
}, {
|
|
1258
|
+
capabilities: {
|
|
1259
|
+
tools: { listChanged: true },
|
|
1260
|
+
prompts: {},
|
|
1261
|
+
resources: {},
|
|
1262
|
+
},
|
|
1263
|
+
instructions: buildServerInstructions(),
|
|
1182
1264
|
});
|
|
1183
1265
|
// Register prompts for common workflows
|
|
1184
1266
|
server.prompt("audit-site", "Run a comprehensive site audit with empathy, agent-readiness, and visual baseline", async () => ({
|
|
@@ -1210,17 +1292,17 @@ SELF-HELP:
|
|
|
1210
1292
|
}));
|
|
1211
1293
|
registerUiResources(server);
|
|
1212
1294
|
// Register resources
|
|
1213
|
-
server.resource("docs://tools-overview", "docs://tools-overview", { description:
|
|
1295
|
+
server.resource("docs://tools-overview", "docs://tools-overview", { description: `Overview of all ${catalogCounts().tools} CBrowser MCP tools organized by category`, mimeType: "text/plain" }, async () => ({
|
|
1214
1296
|
contents: [{
|
|
1215
1297
|
uri: "docs://tools-overview",
|
|
1216
|
-
text:
|
|
1298
|
+
text: buildToolsOverviewText(),
|
|
1217
1299
|
mimeType: "text/plain",
|
|
1218
1300
|
}],
|
|
1219
1301
|
}));
|
|
1220
|
-
server.resource("docs://personas", "docs://personas", { description:
|
|
1302
|
+
server.resource("docs://personas", "docs://personas", { description: `List of ${builtinPersonaCount()} built-in personas`, mimeType: "text/plain" }, async () => ({
|
|
1221
1303
|
contents: [{
|
|
1222
1304
|
uri: "docs://personas",
|
|
1223
|
-
text:
|
|
1305
|
+
text: buildPersonasOverviewText(),
|
|
1224
1306
|
mimeType: "text/plain",
|
|
1225
1307
|
}],
|
|
1226
1308
|
}));
|
|
@@ -1370,11 +1452,7 @@ async function handleMcpRequest(req, res, transport) {
|
|
|
1370
1452
|
}
|
|
1371
1453
|
if (keyHash) {
|
|
1372
1454
|
const cmsUrl = process.env.CMS_URL || "http://localhost:3200";
|
|
1373
|
-
const deductParams =
|
|
1374
|
-
key_hash: keyHash,
|
|
1375
|
-
tool: toolName,
|
|
1376
|
-
...(targetUrl ? { target_url: targetUrl } : {}),
|
|
1377
|
-
});
|
|
1455
|
+
const deductParams = deductQuery(keyHash, toolName, targetUrl);
|
|
1378
1456
|
try {
|
|
1379
1457
|
const creditRes = await fetch(`${cmsUrl}/api/credits/deduct?${deductParams}`, {
|
|
1380
1458
|
method: "POST",
|
|
@@ -1415,7 +1493,7 @@ async function handleMcpRequest(req, res, transport) {
|
|
|
1415
1493
|
// above either returned already (allowed:false) or are fail-open
|
|
1416
1494
|
// (a response with no `allowed` field at all, e.g. an internal-auth
|
|
1417
1495
|
// failure) — neither charged the account, so neither sets this.
|
|
1418
|
-
if (creditRes.ok
|
|
1496
|
+
if (chargedByDeduct(creditRes.ok, data)) {
|
|
1419
1497
|
chargedKeyHash = keyHash;
|
|
1420
1498
|
chargedTool = toolName;
|
|
1421
1499
|
}
|
|
@@ -1515,6 +1593,7 @@ async function handleMcpRequest(req, res, transport) {
|
|
|
1515
1593
|
* @param options - Optional configuration including tool extension callback
|
|
1516
1594
|
*/
|
|
1517
1595
|
export async function startRemoteMcpServer(options) {
|
|
1596
|
+
setCreditChargeMode(options?.chargeCredits === false ? "none" : "charge");
|
|
1518
1597
|
// Auto-initialize all data directories on server start
|
|
1519
1598
|
ensureDirectories();
|
|
1520
1599
|
// Session reaper + rate-limit sweeper. Started HERE rather than at module
|
|
@@ -1561,7 +1640,13 @@ export async function startRemoteMcpServer(options) {
|
|
|
1561
1640
|
console.log(` - Enterprise: unlimited`);
|
|
1562
1641
|
}
|
|
1563
1642
|
console.log(`Listening on ${host}:${port}`);
|
|
1564
|
-
|
|
1643
|
+
// Every HTTP request runs in its own identity context, so the key hash, tier
|
|
1644
|
+
// and API key the /mcp branch sets for it belong to it alone. They were module
|
|
1645
|
+
// globals, set while the request's server was built and read when its tool
|
|
1646
|
+
// ran, several awaits later; an overlapping request from another account
|
|
1647
|
+
// overwrote them in between. The context starts as an unauthenticated caller
|
|
1648
|
+
// at the deployment's default tier. (sec-identity, 2026-10-10)
|
|
1649
|
+
const httpServer = createServer((req, res) => withRequestIdentity({ keyHash: null, tier: effectiveTier(null), apiKey: undefined }, async () => {
|
|
1565
1650
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
1566
1651
|
// Security headers (defense in depth)
|
|
1567
1652
|
res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
|
|
@@ -1903,11 +1988,14 @@ if (urlParams.get('error')) {
|
|
|
1903
1988
|
}
|
|
1904
1989
|
// llms.txt - AI-readable documentation (WebMCP standard)
|
|
1905
1990
|
if (url.pathname === "/llms.txt" || url.pathname === "/.well-known/llms.txt") {
|
|
1991
|
+
// Count and category list derived from TOOL_CATEGORIES. The hand-written
|
|
1992
|
+
// block said 91 tools, and its per-category numbers did not match the
|
|
1993
|
+
// names it listed ("Visual Testing (6)" named five). (B33, 2026-10-10)
|
|
1906
1994
|
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
|
|
1907
1995
|
res.end(`# CBrowser MCP Server
|
|
1908
1996
|
# AI-powered browser automation with cognitive user simulation
|
|
1909
1997
|
|
|
1910
|
-
> CBrowser is an MCP server that provides
|
|
1998
|
+
> CBrowser is an MCP server that provides ${catalogCounts().tools} tools for browser automation,
|
|
1911
1999
|
> visual testing, cognitive user simulation, and accessibility auditing.
|
|
1912
2000
|
> It simulates how real humans think, struggle, and give up on websites.
|
|
1913
2001
|
|
|
@@ -1930,17 +2018,8 @@ POST /mcp - Model Context Protocol endpoint (JSON-RPC 2.0 over SSE)
|
|
|
1930
2018
|
- API Key authentication for programmatic access
|
|
1931
2019
|
- Demo server (demo.cbrowser.ai): Rate-limited, no auth required
|
|
1932
2020
|
|
|
1933
|
-
## Tool Categories (
|
|
1934
|
-
|
|
1935
|
-
- Interaction (5): click, smart_click, fill, scroll, press_key
|
|
1936
|
-
- Analysis (4): analyze_page, extract, find_element_by_intent, assert
|
|
1937
|
-
- Visual Testing (6): visual_baseline, visual_regression, cross_browser_test, responsive_test, ab_comparison
|
|
1938
|
-
- Cognitive (3): cognitive_journey_init, cognitive_journey_update_state, list_cognitive_personas
|
|
1939
|
-
- Personas (10): compare_personas_init, persona_create, persona_traits_list, etc.
|
|
1940
|
-
- Testing (5): nl_test_file, nl_test_inline, repair_test, detect_flaky_tests, coverage_map
|
|
1941
|
-
- Audit (4): agent_ready_audit, competitive_benchmark, empathy_audit, webmcp_ready_audit
|
|
1942
|
-
- Session (4): save_session, load_session, list_sessions, delete_session
|
|
1943
|
-
- Browser (4): browser_health, browser_recover, reset_browser, status
|
|
2021
|
+
## Tool Categories (${catalogCounts().tools} tools)
|
|
2022
|
+
${TOOL_CATEGORIES.map((cat) => `- ${cat.name} (${cat.tools.length}): ${cat.tools.join(", ")}`).join("\n")}
|
|
1944
2023
|
|
|
1945
2024
|
## Links
|
|
1946
2025
|
- Documentation: https://cbrowser.ai/docs
|
|
@@ -1987,8 +2066,8 @@ ${VERSION}
|
|
|
1987
2066
|
}
|
|
1988
2067
|
}</code></pre>
|
|
1989
2068
|
|
|
1990
|
-
<h2>Available Tools (
|
|
1991
|
-
<p>This server provides
|
|
2069
|
+
<h2>Available Tools (${catalogCounts().tools})</h2>
|
|
2070
|
+
<p>This server provides ${catalogCounts().tools} MCP tools for browser automation, testing, and analysis.</p>
|
|
1992
2071
|
<ul>
|
|
1993
2072
|
<li><strong>Navigation:</strong> navigate, screenshot</li>
|
|
1994
2073
|
<li><strong>Interaction:</strong> click, smart_click, fill, scroll, press_key</li>
|
|
@@ -2253,6 +2332,21 @@ ${VERSION}
|
|
|
2253
2332
|
// Log session handling for debugging (truncate ID for readability)
|
|
2254
2333
|
const shortId = browserSessionId.substring(0, 8);
|
|
2255
2334
|
console.log(`[Session] ${sessionSource}: ${shortId}... (active: ${activeSessions.size})`);
|
|
2335
|
+
// This request's caller, written into its own identity context (entered at
|
|
2336
|
+
// createServer above). Set before the transport branch so a reused
|
|
2337
|
+
// stateful transport gets it too: that path used to run on whatever the
|
|
2338
|
+
// last request to build a server had left in the globals. Building a new
|
|
2339
|
+
// server below sets the tier again during registration, as it always did.
|
|
2340
|
+
// (sec-identity, 2026-10-10)
|
|
2341
|
+
const requestApiKey = extractApiKey(req);
|
|
2342
|
+
const resolvedTier = requestApiKey ? await resolveApiKeyTier(requestApiKey) : null;
|
|
2343
|
+
// Key hash for usage tracking: a cbk_ key's own hash, or the key an OAuth
|
|
2344
|
+
// token was issued for; null otherwise. The same derivation as billing.
|
|
2345
|
+
setActiveKeyHash(await resolveRequestKeyHash(req));
|
|
2346
|
+
// Raw key for account-scoped features (custom personas, etc.)
|
|
2347
|
+
const { setSessionApiKey } = await import("./mcp-tools/base/cognitive-tools.js");
|
|
2348
|
+
setSessionApiKey(requestApiKey || undefined);
|
|
2349
|
+
setTierGate(effectiveTier(resolvedTier));
|
|
2256
2350
|
// Now handle transport (stateful mode caches transport, stateless creates new each time)
|
|
2257
2351
|
const hasExistingTransport = existingSessionId ? transports.has(existingSessionId) : false;
|
|
2258
2352
|
if (sessionMode === "stateful" && existingSessionId && hasExistingTransport) {
|
|
@@ -2266,30 +2360,6 @@ ${VERSION}
|
|
|
2266
2360
|
transport = new StreamableHTTPServerTransport({
|
|
2267
2361
|
sessionIdGenerator: sessionMode === "stateful" ? () => browserSessionId : undefined,
|
|
2268
2362
|
});
|
|
2269
|
-
// Resolve pricing tier from API key (cbk_ keys → CMS lookup)
|
|
2270
|
-
const requestApiKey = extractApiKey(req);
|
|
2271
|
-
const resolvedTier = requestApiKey ? await resolveApiKeyTier(requestApiKey) : null;
|
|
2272
|
-
// Set key hash for usage tracking
|
|
2273
|
-
if (requestApiKey?.startsWith("cbk_")) {
|
|
2274
|
-
const { createHash } = await import("crypto");
|
|
2275
|
-
setActiveKeyHash(createHash("sha256").update(requestApiKey).digest("hex"));
|
|
2276
|
-
}
|
|
2277
|
-
else if (requestApiKey) {
|
|
2278
|
-
// Check token→keyHash map (OAuth tokens that aren't raw cbk_ keys)
|
|
2279
|
-
const mapped = tokenToKeyHash.get(requestApiKey);
|
|
2280
|
-
if (mapped && mapped.expiresAt > Date.now()) {
|
|
2281
|
-
setActiveKeyHash(mapped.keyHash);
|
|
2282
|
-
}
|
|
2283
|
-
else {
|
|
2284
|
-
setActiveKeyHash(null);
|
|
2285
|
-
}
|
|
2286
|
-
}
|
|
2287
|
-
else {
|
|
2288
|
-
setActiveKeyHash(null);
|
|
2289
|
-
}
|
|
2290
|
-
// Set session API key for account-scoped features (custom personas, etc.)
|
|
2291
|
-
const { setSessionApiKey } = await import("./mcp-tools/base/cognitive-tools.js");
|
|
2292
|
-
setSessionApiKey(requestApiKey || undefined);
|
|
2293
2363
|
// Create and connect server with session isolation and tier gating
|
|
2294
2364
|
const server = createMcpServer(options?.registerTools, browserSessionId, resolvedTier);
|
|
2295
2365
|
// Allow extension with additional tools (for Enterprise)
|
|
@@ -2347,7 +2417,7 @@ ${VERSION}
|
|
|
2347
2417
|
// 404 for other paths
|
|
2348
2418
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
2349
2419
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
2350
|
-
});
|
|
2420
|
+
}));
|
|
2351
2421
|
httpServer.listen(port, host, () => {
|
|
2352
2422
|
void (async () => {
|
|
2353
2423
|
console.log(`\nCognitive Browser Remote MCP Server running at http://${host}:${port}`);
|