@skyramp/mcp 0.4.4 → 0.5.0-rc.10
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/build/entitlements/entitlementGate.d.ts +8 -0
- package/build/entitlements/entitlementGate.js +57 -0
- package/build/entitlements/runCodes.d.ts +18 -0
- package/build/entitlements/runCodes.js +16 -0
- package/build/index.js +32 -6
- package/build/playwright/PlaywrightTraceService.d.ts +17 -0
- package/build/playwright/PlaywrightTraceService.js +29 -1
- package/build/playwright/dataSafetyConfig.d.ts +74 -0
- package/build/playwright/dataSafetyConfig.js +109 -0
- package/build/playwright/essentialTools.d.ts +1 -0
- package/build/playwright/essentialTools.js +44 -0
- package/build/playwright/registerPlaywrightTools.d.ts +36 -1
- package/build/playwright/registerPlaywrightTools.js +87 -43
- package/build/playwright/safeResolveDataSafety.d.ts +24 -0
- package/build/playwright/safeResolveDataSafety.js +33 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.d.ts +17 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +46 -177
- package/build/prompts/modularization/ui-test-modularization.d.ts +1 -0
- package/build/prompts/modularization/ui-test-modularization.js +9 -2
- package/build/prompts/test-maintenance/driftAnalysisShared.js +1 -1
- package/build/services/FleetExecutionService.d.ts +87 -16
- package/build/services/FleetExecutionService.js +487 -48
- package/build/services/FleetRunRegistry.d.ts +49 -0
- package/build/services/FleetRunRegistry.js +178 -0
- package/build/services/containerEnv.js +8 -2
- package/build/services/fleetLimitConfirmation.d.ts +19 -0
- package/build/services/fleetLimitConfirmation.js +126 -0
- package/build/tool-phases.d.ts +14 -8
- package/build/tool-phases.js +88 -43
- package/build/tools/code-refactor/assertion-state.js +3 -1
- package/build/tools/code-refactor/codeReuseTool.js +2 -1
- package/build/tools/code-refactor/utils-verify-gates.js +1 -1
- package/build/tools/executeFleetTool.d.ts +6 -4
- package/build/tools/executeFleetTool.js +67 -23
- package/build/tools/fixErrorTool.js +2 -1
- package/build/tools/fleetLifecycleTools.d.ts +20 -0
- package/build/tools/fleetLifecycleTools.js +124 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.d.ts +38 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.js +7 -1
- package/build/tools/generate-tests/generateUILoadRestTool.d.ts +19 -1
- package/build/tools/generate-tests/generateUILoadRestTool.js +316 -50
- package/build/tools/submitReportTool.js +4 -122
- package/build/tools/test-management/registerTestPlanTool.js +22 -6
- package/build/tools/workspace/initScanWorkspaceTool.d.ts +11 -1
- package/build/tools/workspace/initScanWorkspaceTool.js +111 -9
- package/build/tools/workspace/initializeWorkspaceTool.js +40 -4
- package/build/types/AssertionOutcome.d.ts +3 -0
- package/build/types/TestbotReport.d.ts +0 -37
- package/build/types/TestbotReport.js +0 -22
- package/build/types/index.d.ts +1 -1
- package/build/types/index.js +1 -1
- package/build/utils/assertion-verify/created-add-guard.d.ts +12 -0
- package/build/utils/assertion-verify/created-add-guard.js +173 -0
- package/build/utils/assertion-verify/index.d.ts +1 -0
- package/build/utils/assertion-verify/index.js +1 -0
- package/build/utils/assertion-verify/verify.d.ts +7 -3
- package/build/utils/assertion-verify/verify.js +9 -3
- package/build/utils/featureFlags.js +6 -3
- package/build/utils/repositorySlug.d.ts +14 -0
- package/build/utils/repositorySlug.js +46 -1
- package/build/utils/telemetry.d.ts +2 -0
- package/build/utils/telemetry.js +12 -0
- package/build/utils/utils-verify/action-key.d.ts +59 -2
- package/build/utils/utils-verify/action-key.js +162 -6
- package/build/utils/utils-verify/action-sites.d.ts +7 -0
- package/build/utils/utils-verify/action-sites.js +277 -35
- package/build/utils/utils-verify/call-sites.d.ts +8 -2
- package/build/utils/utils-verify/language-spec.js +1 -1
- package/build/utils/utils-verify/parse.d.ts +4 -0
- package/build/utils/utils-verify/parse.js +2 -1
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +63 -121
- package/build/workspace/workspace.js +45 -48
- package/build/workspace/workspaceVerifier.d.ts +22 -0
- package/build/workspace/workspaceVerifier.js +65 -0
- package/node_modules/playwright/lib/dom-analyzer/blueprint.js +7 -6
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.js +5 -4
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.test.js +12 -0
- package/node_modules/playwright/lib/mcp/skyramp/dataSafetyGuard.js +822 -0
- package/node_modules/playwright/lib/mcp/skyramp/dataSafetySession.js +372 -0
- package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +101 -8
- package/node_modules/playwright/lib/mcp/skyramp/finishTestSetupTool.js +45 -0
- package/node_modules/playwright/lib/mcp/skyramp/index.js +16 -0
- package/node_modules/playwright/lib/mcp/skyramp/recordingPhase.js +65 -0
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +1 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +598 -127
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +85 -31
- package/node_modules/playwright/lib/mcp/test/skyRampExport.test.js +132 -5
- package/node_modules/playwright/node_modules/playwright-core/lib/cli/program.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/client/browserContext.js +4 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/protocol/validator.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/browserContext.js +17 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +1 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/har/harRecorder.js +1 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/har/harTracer.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderRunner.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderSignalProcessor.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/clickExpandedState.js +75 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder.js +4 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/utils/isomorphic/volatileDate.js +85 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-Bou8kKzE.js → codeMirrorModule-BuhAdckO.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-COAHN430.js +809 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.0MJW0uG7.js → index.7AU8h0PZ.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.GLqsx5cI.js → uiMode.Dl7smBJn.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/cli/program.ts +5 -0
- package/node_modules/playwright/node_modules/playwright-core/src/client/browserContext.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/client/types.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/protocol/validator.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/browserContext.ts +25 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/har/harRecorder.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/har/harTracer.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderRunner.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderSignalProcessor.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/clickExpandedState.ts +82 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/utils/isomorphic/volatileDate.ts +137 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +4 -4
- package/plugin/prompts/plan-tests.md +2 -2
- package/plugin/prompts/testbot-task1.md +1 -1
- package/plugin/skills/enhance-assertions/reference/integration.md +1 -0
- package/plugin/skills/setup-testbot-target-sut/SKILL.md +60 -0
- package/plugin/skills/setup-testbot-target-sut/reference/ci-workflow.md +26 -0
- package/plugin/skills/setup-testbot-target-sut/reference/deployed-environment.md +105 -0
- package/plugin/skills/setup-testbot-target-sut/reference/existing-tests.md +14 -0
- package/plugin/skills/setup-testbot-target-sut/reference/troubleshooting.md +20 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.d.ts +0 -2
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +0 -178
- package/build/prompts/sut-setup/modes/dockerComposePrompt.d.ts +0 -2
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +0 -311
- package/build/prompts/sut-setup/shared.d.ts +0 -62
- package/build/prompts/sut-setup/shared.js +0 -196
- package/build/resources/sutSetupResource.d.ts +0 -2
- package/build/resources/sutSetupResource.js +0 -63
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-DTp2-8Si.js +0 -809
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/server";
|
|
2
|
+
import { type RunCodes } from "./runCodes.js";
|
|
3
|
+
/**
|
|
4
|
+
* Makes every tool registered on `server` after this call check the run's
|
|
5
|
+
* entitlement codes before it runs. Without a run token it does nothing, so
|
|
6
|
+
* every tool behaves as before. Must be called before any tool is registered.
|
|
7
|
+
*/
|
|
8
|
+
export declare function installEntitlementGate(server: McpServer, readCodes?: () => Promise<RunCodes>, token?: string | undefined): void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readRunCodes } from "./runCodes.js";
|
|
2
|
+
import { NOT_GATED, TOOLS } from "../tool-phases.js";
|
|
3
|
+
const FEATURE_NAMES = {
|
|
4
|
+
analyze: "change analysis",
|
|
5
|
+
recommend: "test recommendations",
|
|
6
|
+
generate: "test generation",
|
|
7
|
+
execute: "test execution",
|
|
8
|
+
};
|
|
9
|
+
function refusal(text) {
|
|
10
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
11
|
+
}
|
|
12
|
+
/** Returns why `tool` may not run with these codes, or undefined if it may. */
|
|
13
|
+
function refusalReason(tool, code, run) {
|
|
14
|
+
if ("refused" in run) {
|
|
15
|
+
return `Tool ${tool} is not available: Skyramp could not confirm what this run is allowed to do. ${run.refused}`;
|
|
16
|
+
}
|
|
17
|
+
if (run.codes.has(code))
|
|
18
|
+
return undefined;
|
|
19
|
+
return `Tool ${tool} is not available: your Skyramp plan does not include ${FEATURE_NAMES[code]}.`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Makes every tool registered on `server` after this call check the run's
|
|
23
|
+
* entitlement codes before it runs. Without a run token it does nothing, so
|
|
24
|
+
* every tool behaves as before. Must be called before any tool is registered.
|
|
25
|
+
*/
|
|
26
|
+
export function installEntitlementGate(server, readCodes = readRunCodes, token = process.env.SKYRAMP_RUN_TOKEN) {
|
|
27
|
+
if (!token?.trim())
|
|
28
|
+
return;
|
|
29
|
+
let codes;
|
|
30
|
+
const runCodes = () => (codes ??= readCodes()
|
|
31
|
+
.catch((err) => ({
|
|
32
|
+
refused: err instanceof Error ? err.message : String(err),
|
|
33
|
+
}))
|
|
34
|
+
.then((run) => {
|
|
35
|
+
// A refusal can be temporary, so the next tool call reads again.
|
|
36
|
+
if ("refused" in run)
|
|
37
|
+
codes = undefined;
|
|
38
|
+
return run;
|
|
39
|
+
}));
|
|
40
|
+
// Typed loosely: the gate forwards every argument as it came, whatever
|
|
41
|
+
// overload the tool used.
|
|
42
|
+
const target = server;
|
|
43
|
+
const register = target.registerTool.bind(target);
|
|
44
|
+
target.registerTool = (name, config, cb) => {
|
|
45
|
+
const code = TOOLS[name]?.entitlement;
|
|
46
|
+
if (code === NOT_GATED)
|
|
47
|
+
return register(name, config, cb);
|
|
48
|
+
const handler = cb;
|
|
49
|
+
return register(name, config, async (...args) => {
|
|
50
|
+
if (code === undefined) {
|
|
51
|
+
return refusal(`Tool ${name} is not available in this run.`);
|
|
52
|
+
}
|
|
53
|
+
const reason = refusalReason(name, code, await runCodes());
|
|
54
|
+
return reason ? refusal(reason) : handler(...args);
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type RunCodes = {
|
|
2
|
+
codes: ReadonlySet<string>;
|
|
3
|
+
}
|
|
4
|
+
/** Core refused the run token; `refused` is its reason. */
|
|
5
|
+
| {
|
|
6
|
+
refused: string;
|
|
7
|
+
};
|
|
8
|
+
type Entitlements = {
|
|
9
|
+
codes: string[];
|
|
10
|
+
} | {
|
|
11
|
+
error: string;
|
|
12
|
+
};
|
|
13
|
+
/** The part of SkyrampClient this file uses. Core releases before getEntitlements lack it. */
|
|
14
|
+
export interface Core {
|
|
15
|
+
getEntitlements?: (repository: string) => Promise<Entitlements>;
|
|
16
|
+
}
|
|
17
|
+
export declare function readRunCodes(core?: Core, env?: NodeJS.ProcessEnv): Promise<RunCodes>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The only file that may ask Skyramp core for the run's entitlement codes.
|
|
3
|
+
* Everything else in MCP reads them through `readRunCodes`, so a change on the
|
|
4
|
+
* core side changes this file and nothing else.
|
|
5
|
+
*/
|
|
6
|
+
import { SkyrampClient } from "@skyramp/skyramp";
|
|
7
|
+
import { runRepository } from "../utils/repositorySlug.js";
|
|
8
|
+
export async function readRunCodes(core = new SkyrampClient(), env = process.env) {
|
|
9
|
+
if (!core.getEntitlements) {
|
|
10
|
+
return { refused: "This Skyramp version does not support it yet." };
|
|
11
|
+
}
|
|
12
|
+
const answer = await core.getEntitlements(runRepository(env) ?? "");
|
|
13
|
+
return "error" in answer
|
|
14
|
+
? { refused: answer.error }
|
|
15
|
+
: { codes: new Set(answer.codes) };
|
|
16
|
+
}
|
package/build/index.js
CHANGED
|
@@ -7,9 +7,12 @@ import { registerTraceTool } from "./tools/trace/startTraceCollectionTool.js";
|
|
|
7
7
|
import { registerTraceStopTool } from "./tools/trace/stopTraceCollectionTool.js";
|
|
8
8
|
import { registerExecuteSkyrampTestTool } from "./tools/executeSkyrampTestTool.js";
|
|
9
9
|
import { registerExecuteFleetTool } from "./tools/executeFleetTool.js";
|
|
10
|
+
import { registerFleetLifecycleTools } from "./tools/fleetLifecycleTools.js";
|
|
11
|
+
import { fleetRuns } from "./services/FleetRunRegistry.js";
|
|
10
12
|
import { AUTH_PLACEHOLDER_TOKEN } from "./types/TestTypes.js";
|
|
11
13
|
import { AUTH_CONFLICT_ERROR_MSG } from "./prompts/test-recommendation/recommendationSections.js";
|
|
12
14
|
import { logger } from "./utils/logger.js";
|
|
15
|
+
import { installEntitlementGate } from "./entitlements/entitlementGate.js";
|
|
13
16
|
import { registerUITestTool } from "./tools/generate-tests/generateUIRestTool.js";
|
|
14
17
|
import { registerUILoadTestTool } from "./tools/generate-tests/generateUILoadRestTool.js";
|
|
15
18
|
import { registerSmokeTestTool } from "./tools/generate-tests/generateSmokeRestTool.js";
|
|
@@ -33,7 +36,6 @@ import { registerMockTool } from "./tools/generate-tests/generateMockRestTool.js
|
|
|
33
36
|
import { registerBatchMockTool } from "./tools/generate-tests/batchMockGenerationTool.js";
|
|
34
37
|
import { registerAnalyzeChangesTool, registerAnalyzeTestHealthTool, registerActionsTool, registerRegisterTestPlanTool, registerResolveScreenTool, } from "./tools/test-management/index.js";
|
|
35
38
|
import { registerTestbotPrompt } from "./prompts/testbot/testbot-prompts.js";
|
|
36
|
-
import { registerSutSetupResource } from "./resources/sutSetupResource.js";
|
|
37
39
|
import { registerTestbotResource } from "./resources/testbotResource.js";
|
|
38
40
|
import { registerLocalDevPrompt } from "./prompts/local-dev/local-dev-prompts.js";
|
|
39
41
|
import { registerSubmitReportTool } from "./tools/submitReportTool.js";
|
|
@@ -53,6 +55,7 @@ import { isTestbotEnabled, isLocalDevEnabled, isSkillsLoaded, } from "./utils/fe
|
|
|
53
55
|
import { findRoot, readTestIdAttribute } from "./utils/pom-scope/index.js";
|
|
54
56
|
import { discoverTestIdAttribute } from "./utils/pom-scope/testIdDiscovery.js";
|
|
55
57
|
import { registerPlaywrightTools, registerTraceRecordingPrompt, getPlaywrightTraceService, } from "./playwright/index.js";
|
|
58
|
+
import { resolveDataSafetyOptions, playwrightToolOptionsFor, } from "./playwright/dataSafetyConfig.js";
|
|
56
59
|
// One-click routing instructions — injected into MCP server instructions so agents
|
|
57
60
|
// know to delegate to the one-click tool rather than self-orchestrating.
|
|
58
61
|
// Suppressed in testbot mode where the testbot prompt drives orchestration.
|
|
@@ -74,7 +77,7 @@ else if (localDevEnabled) {
|
|
|
74
77
|
oneClickInstructions = localDevInstructions;
|
|
75
78
|
}
|
|
76
79
|
// Global workspace-init nudge for bare MCP usage. Suppressed in testbot mode,
|
|
77
|
-
// which
|
|
80
|
+
// which initializes the workspace in its own steps instead.
|
|
78
81
|
const workspaceInitInstructions = isTestbotEnabled() || oneClickEnabled || localDevEnabled
|
|
79
82
|
? ""
|
|
80
83
|
: `
|
|
@@ -156,6 +159,8 @@ async function createServer({ era }) {
|
|
|
156
159
|
8. The user can always override workspace defaults by explicitly specifying values in their request.
|
|
157
160
|
`,
|
|
158
161
|
});
|
|
162
|
+
// Before any tool registers, so every tool goes through the check.
|
|
163
|
+
installEntitlementGate(server);
|
|
159
164
|
// Register prompts
|
|
160
165
|
logger.info("Starting prompt registration process");
|
|
161
166
|
const prompts = [
|
|
@@ -167,8 +172,6 @@ async function createServer({ era }) {
|
|
|
167
172
|
prompts.push(registerTestbotPrompt);
|
|
168
173
|
registerTestbotResource(server);
|
|
169
174
|
logger.info("Testbot prompt enabled via SKYRAMP_FEATURE_TESTBOT");
|
|
170
|
-
registerSutSetupResource(server);
|
|
171
|
-
logger.info("Testbot SUT setup resource enabled via SKYRAMP_FEATURE_TESTBOT");
|
|
172
175
|
}
|
|
173
176
|
if (isLocalDevEnabled()) {
|
|
174
177
|
prompts.push(registerLocalDevPrompt);
|
|
@@ -242,6 +245,7 @@ async function createServer({ era }) {
|
|
|
242
245
|
registerLogoutTool,
|
|
243
246
|
registerExecuteSkyrampTestTool,
|
|
244
247
|
registerExecuteFleetTool,
|
|
248
|
+
registerFleetLifecycleTools,
|
|
245
249
|
registerTraceTool,
|
|
246
250
|
registerTraceStopTool,
|
|
247
251
|
];
|
|
@@ -256,6 +260,11 @@ async function createServer({ era }) {
|
|
|
256
260
|
infrastructureTools.forEach((registerTool) => registerTool(server));
|
|
257
261
|
// Register Playwright browser tools (trace recording via browser automation).
|
|
258
262
|
//
|
|
263
|
+
// PlaywrightTraceService (and the fork's network-layer data-safety guard it
|
|
264
|
+
// wraps) is constructed here, before any per-call `repositoryPath` exists,
|
|
265
|
+
// and `browser_navigate` itself takes no `repositoryPath` param, so
|
|
266
|
+
// process.cwd() is the only path data-safety config resolves from.
|
|
267
|
+
//
|
|
259
268
|
// Tell the capture which attribute this repository uses for test ids. Without
|
|
260
269
|
// it the capture assumes Playwright's `data-testid`, and a repo using anything
|
|
261
270
|
// else gets no test-id anchor and a positional xpath for every element — which
|
|
@@ -312,7 +321,13 @@ async function createServer({ era }) {
|
|
|
312
321
|
error: err,
|
|
313
322
|
});
|
|
314
323
|
}
|
|
315
|
-
|
|
324
|
+
// SKYR-4047: thread data-safety options; resolveDataSafetyOptions returns
|
|
325
|
+
// undefined in the testbot lane (SKYRAMP_DATA_SAFETY env) so the fork reads env.
|
|
326
|
+
const dataSafety = await resolveDataSafetyOptions(process.cwd());
|
|
327
|
+
await registerPlaywrightTools(server, {
|
|
328
|
+
...playwrightToolOptionsFor(dataSafety),
|
|
329
|
+
testIdAttribute,
|
|
330
|
+
});
|
|
316
331
|
})().catch((err) => {
|
|
317
332
|
logger.error("Failed to register Playwright tools", { error: err });
|
|
318
333
|
});
|
|
@@ -328,11 +343,18 @@ async function createServer({ era }) {
|
|
|
328
343
|
runInitTrigger();
|
|
329
344
|
return server;
|
|
330
345
|
}
|
|
346
|
+
// A crashing process must not stay alive anywhere near the full graceful
|
|
347
|
+
// shutdown's 30s grace — it still gives workers a moment to cancel, but caps
|
|
348
|
+
// it low enough that a crash can't hang the process.
|
|
349
|
+
const CRASH_CLEANUP_WAIT_MS = 3_000;
|
|
350
|
+
/** Interrupt running fleets (bounded wait) so an exit does not leave workers loading. */
|
|
351
|
+
const cancelFleets = () => fleetRuns.cancelAll(CRASH_CLEANUP_WAIT_MS).catch(() => { });
|
|
331
352
|
// Global error handlers for crash telemetry
|
|
332
353
|
process.on("uncaughtException", async (error) => {
|
|
333
354
|
// Don't send analytics for EPIPE errors
|
|
334
355
|
const isEpipeError = "code" in error && error.code === "EPIPE";
|
|
335
356
|
if (isEpipeError) {
|
|
357
|
+
await cancelFleets();
|
|
336
358
|
process.exit(0);
|
|
337
359
|
}
|
|
338
360
|
logger.critical("Uncaught exception - MCP server crashing", {
|
|
@@ -345,6 +367,7 @@ process.on("uncaughtException", async (error) => {
|
|
|
345
367
|
catch (telemetryError) {
|
|
346
368
|
logger.error("Failed to send crash telemetry", { telemetryError });
|
|
347
369
|
}
|
|
370
|
+
await cancelFleets();
|
|
348
371
|
process.exit(1);
|
|
349
372
|
});
|
|
350
373
|
// Handle unhandled promise rejections — without this handler Node.js 15+
|
|
@@ -354,6 +377,7 @@ process.on("unhandledRejection", async (reason) => {
|
|
|
354
377
|
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
355
378
|
// EPIPE means the client already disconnected — exit cleanly
|
|
356
379
|
if ("code" in error && error.code === "EPIPE") {
|
|
380
|
+
await cancelFleets();
|
|
357
381
|
process.exit(0);
|
|
358
382
|
}
|
|
359
383
|
// gRPC/Docker connectivity errors — log and survive.
|
|
@@ -400,7 +424,9 @@ async function main() {
|
|
|
400
424
|
const cleanupPlaywright = playwrightService
|
|
401
425
|
? playwrightService.shutdown().catch(() => { })
|
|
402
426
|
: Promise.resolve();
|
|
403
|
-
|
|
427
|
+
// A running fleet is interrupted so it can cancel its workers, not left loading them.
|
|
428
|
+
const cleanupFleets = fleetRuns.cancelAll().catch(() => { });
|
|
429
|
+
Promise.all([cleanupPlaywright, cleanupFleets]).finally(() => {
|
|
404
430
|
handle.close().finally(() => {
|
|
405
431
|
process.exit(0);
|
|
406
432
|
});
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* to bridge the module systems.
|
|
7
7
|
*/
|
|
8
8
|
import type { CallToolResult } from "@modelcontextprotocol/server";
|
|
9
|
+
import type { DataSafetyOptions } from "./dataSafetyConfig.js";
|
|
9
10
|
interface McpTool {
|
|
10
11
|
name: string;
|
|
11
12
|
description?: string;
|
|
@@ -25,6 +26,8 @@ export interface PlaywrightTraceOptions {
|
|
|
25
26
|
* against, so only this server can supply it.
|
|
26
27
|
*/
|
|
27
28
|
testIdAttribute?: string;
|
|
29
|
+
/** Data safety: the workspace section, for the fork to validate. */
|
|
30
|
+
dataSafety?: DataSafetyOptions;
|
|
28
31
|
}
|
|
29
32
|
export declare class PlaywrightTraceService {
|
|
30
33
|
private _backend;
|
|
@@ -35,6 +38,20 @@ export declare class PlaywrightTraceService {
|
|
|
35
38
|
isInitialized(): boolean;
|
|
36
39
|
getTools(): McpTool[];
|
|
37
40
|
callTool(name: string, args: Record<string, unknown>): Promise<CallToolResult>;
|
|
41
|
+
/**
|
|
42
|
+
* How long shutdown() waits for the fork's session-end teardown, so a hung
|
|
43
|
+
* target cannot hold the process forever. Teardown runs after a 5 s
|
|
44
|
+
* body-read settle, in up to 3 DELETE passes; each DELETE times out after
|
|
45
|
+
* 10 s, and a pass in which every DELETE timed out is not retried. Several
|
|
46
|
+
* hung DELETEs can still use up this bound. The fork writes the ledger after
|
|
47
|
+
* each deletion, so the ledger file is still correct when the bound runs out.
|
|
48
|
+
*/
|
|
49
|
+
static shutdownTeardownTimeoutMs: number;
|
|
50
|
+
/**
|
|
51
|
+
* The fork's serverClosed() starts data-safety teardown and returns at
|
|
52
|
+
* once. The caller exits the process right after this resolves, so wait
|
|
53
|
+
* for that work (bounded) — or the teardown DELETEs never reach the target.
|
|
54
|
+
*/
|
|
38
55
|
shutdown(): Promise<void>;
|
|
39
56
|
}
|
|
40
57
|
export {};
|
|
@@ -27,6 +27,7 @@ export class PlaywrightTraceService {
|
|
|
27
27
|
headless: this._options.headless,
|
|
28
28
|
outputDir: this._options.outputDir || process.cwd(),
|
|
29
29
|
testIdAttribute: this._options.testIdAttribute,
|
|
30
|
+
dataSafety: this._options.dataSafety,
|
|
30
31
|
});
|
|
31
32
|
// Initialize with a synthetic client info
|
|
32
33
|
await this._backend.initialize({
|
|
@@ -64,11 +65,38 @@ export class PlaywrightTraceService {
|
|
|
64
65
|
}
|
|
65
66
|
return this._backend.callTool(name, args);
|
|
66
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* How long shutdown() waits for the fork's session-end teardown, so a hung
|
|
70
|
+
* target cannot hold the process forever. Teardown runs after a 5 s
|
|
71
|
+
* body-read settle, in up to 3 DELETE passes; each DELETE times out after
|
|
72
|
+
* 10 s, and a pass in which every DELETE timed out is not retried. Several
|
|
73
|
+
* hung DELETEs can still use up this bound. The fork writes the ledger after
|
|
74
|
+
* each deletion, so the ledger file is still correct when the bound runs out.
|
|
75
|
+
*/
|
|
76
|
+
static shutdownTeardownTimeoutMs = 30_000;
|
|
77
|
+
/**
|
|
78
|
+
* The fork's serverClosed() starts data-safety teardown and returns at
|
|
79
|
+
* once. The caller exits the process right after this resolves, so wait
|
|
80
|
+
* for that work (bounded) — or the teardown DELETEs never reach the target.
|
|
81
|
+
*/
|
|
67
82
|
async shutdown() {
|
|
68
83
|
if (this._backend) {
|
|
69
|
-
this._backend
|
|
84
|
+
const backend = this._backend;
|
|
70
85
|
this._backend = null;
|
|
71
86
|
this._tools = [];
|
|
87
|
+
backend.serverClosed?.();
|
|
88
|
+
const pending = backend.autoExportPromise;
|
|
89
|
+
if (pending) {
|
|
90
|
+
let timer;
|
|
91
|
+
const bound = new Promise((resolve) => {
|
|
92
|
+
timer = setTimeout(() => {
|
|
93
|
+
logger.warning(`Data-safety teardown did not finish within ${PlaywrightTraceService.shutdownTeardownTimeoutMs} ms; exiting anyway`);
|
|
94
|
+
resolve();
|
|
95
|
+
}, PlaywrightTraceService.shutdownTeardownTimeoutMs);
|
|
96
|
+
});
|
|
97
|
+
await Promise.race([pending.catch(() => { }), bound]);
|
|
98
|
+
clearTimeout(timer);
|
|
99
|
+
}
|
|
72
100
|
logger.info("PlaywrightTraceService shut down");
|
|
73
101
|
}
|
|
74
102
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mcp-side data-safety config resolution + off-limits URL check.
|
|
3
|
+
*
|
|
4
|
+
* Resolution stack — which function a caller should reach for:
|
|
5
|
+
* - `resolveDataSafetyOptions` (this file): reads the workspace.yml
|
|
6
|
+
* `dataSafety` section, honoring the env-var precedence rule below.
|
|
7
|
+
* Lowest-level; used to build the fork's `options` argument.
|
|
8
|
+
* - `effectiveDataSafetyConfig` (this file): resolves through the fork's
|
|
9
|
+
* own `parseDataSafetyConfig` so the result matches exactly what the
|
|
10
|
+
* fork's network-layer guard will enforce. Can throw (unguarded require
|
|
11
|
+
* of the vendored fork).
|
|
12
|
+
* - `safeResolveDataSafety` (safeResolveDataSafety.ts): fail-open wrapper
|
|
13
|
+
* around `effectiveDataSafetyConfig`.
|
|
14
|
+
* - `resolveDataSafetyForRegistration` (registerPlaywrightTools.ts):
|
|
15
|
+
* fail-open wrapper for the enforcement lane specifically — resolves via
|
|
16
|
+
* `safeResolveDataSafety` and additionally normalizes an inert result
|
|
17
|
+
* (mode "off", no offLimits) to `null` for the navigate pre-check.
|
|
18
|
+
*
|
|
19
|
+
* The vendored Playwright fork's DataSafetyGuard/parseDataSafetyConfig read
|
|
20
|
+
* their config with precedence `options ?? env ?? inert`. Testbot resolves
|
|
21
|
+
* the full precedence chain (action input > workspace > auto-default) itself
|
|
22
|
+
* and ships the result via the SKYRAMP_DATA_SAFETY env var, which the fork
|
|
23
|
+
* reads directly. If mcp ALSO passed options derived from the workspace file
|
|
24
|
+
* in that lane, options would win over env and silently override testbot's
|
|
25
|
+
* resolution. So `resolveDataSafetyOptions` returns undefined whenever
|
|
26
|
+
* SKYRAMP_DATA_SAFETY is set — only the env-less lane (local/manual MCP
|
|
27
|
+
* usage, no testbot in the loop) reads the workspace section itself.
|
|
28
|
+
*
|
|
29
|
+
* The dataSafety section is read out of the raw YAML rather than through the
|
|
30
|
+
* full strict workspace schema (see workspace/workspace.ts), so an old or
|
|
31
|
+
* unrelated workspace.yml that fails full schema validation for reasons that
|
|
32
|
+
* have nothing to do with data safety cannot flip anyone to strict by
|
|
33
|
+
* accident. A PRESENT dataSafety section goes to the fork as written, and the
|
|
34
|
+
* fork's validateConfig checks each field on its own: a bad mode fails closed
|
|
35
|
+
* to strict, and a bad offLimits entry stops every tool call, so a bad field
|
|
36
|
+
* never drops the fields next to it. A workspace.yml that does not parse but
|
|
37
|
+
* has a top-level dataSafety key fails closed to strict.
|
|
38
|
+
*/
|
|
39
|
+
export type DataSafetyConfig = {
|
|
40
|
+
mode: "strict" | "readonly" | "off";
|
|
41
|
+
offLimits: string[];
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The fork's `options` argument: the workspace.yml dataSafety section as
|
|
45
|
+
* written, unvalidated. The fork's parseDataSafetyConfig validates it.
|
|
46
|
+
*/
|
|
47
|
+
export type DataSafetyOptions = unknown;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve mcp-side options for the trace backend. Returns undefined when
|
|
50
|
+
* SKYRAMP_DATA_SAFETY is set (testbot's resolution wins — see module
|
|
51
|
+
* docstring) or when no dataSafety signal exists in the workspace file.
|
|
52
|
+
* Only `mode` (absent means "off") and `offLimits` are passed on, so a field
|
|
53
|
+
* added to the section later cannot change what the fork enforces.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveDataSafetyOptions(workspaceRoot: string): Promise<DataSafetyOptions | undefined>;
|
|
56
|
+
/** The config exactly as the fork's parseDataSafetyConfig will resolve it. */
|
|
57
|
+
export declare function effectiveDataSafetyConfig(workspaceRoot: string): Promise<DataSafetyConfig>;
|
|
58
|
+
/** offLimits check via the fork's own pure matcher so mcp and fork can never disagree. */
|
|
59
|
+
export declare function isOffLimitsUrl(url: string, config: DataSafetyConfig): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Maps a resolved data-safety config to the `options` argument
|
|
62
|
+
* `registerPlaywrightTools` expects, for the enforcement-lane wiring in
|
|
63
|
+
* index.ts: `resolveDataSafetyOptions(cwd).then(ds => registerPlaywrightTools(server, playwrightToolOptionsFor(ds)))`.
|
|
64
|
+
* Extracted (rather than an inline arrow at the call site) so the mapping is
|
|
65
|
+
* directly unit-testable without booting the McpServer — mirrors
|
|
66
|
+
* `resolveDataSafetyForRegistration`'s existing test approach
|
|
67
|
+
* (registerPlaywrightTools.dataSafety.test.ts). The return type is left
|
|
68
|
+
* structural (not `PlaywrightTraceOptions`) to avoid a circular import with
|
|
69
|
+
* PlaywrightTraceService.ts, which itself imports `DataSafetyConfig` from
|
|
70
|
+
* this module.
|
|
71
|
+
*/
|
|
72
|
+
export declare function playwrightToolOptionsFor(dataSafety: DataSafetyOptions | undefined): {
|
|
73
|
+
dataSafety: DataSafetyOptions;
|
|
74
|
+
} | undefined;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mcp-side data-safety config resolution + off-limits URL check.
|
|
3
|
+
*
|
|
4
|
+
* Resolution stack — which function a caller should reach for:
|
|
5
|
+
* - `resolveDataSafetyOptions` (this file): reads the workspace.yml
|
|
6
|
+
* `dataSafety` section, honoring the env-var precedence rule below.
|
|
7
|
+
* Lowest-level; used to build the fork's `options` argument.
|
|
8
|
+
* - `effectiveDataSafetyConfig` (this file): resolves through the fork's
|
|
9
|
+
* own `parseDataSafetyConfig` so the result matches exactly what the
|
|
10
|
+
* fork's network-layer guard will enforce. Can throw (unguarded require
|
|
11
|
+
* of the vendored fork).
|
|
12
|
+
* - `safeResolveDataSafety` (safeResolveDataSafety.ts): fail-open wrapper
|
|
13
|
+
* around `effectiveDataSafetyConfig`.
|
|
14
|
+
* - `resolveDataSafetyForRegistration` (registerPlaywrightTools.ts):
|
|
15
|
+
* fail-open wrapper for the enforcement lane specifically — resolves via
|
|
16
|
+
* `safeResolveDataSafety` and additionally normalizes an inert result
|
|
17
|
+
* (mode "off", no offLimits) to `null` for the navigate pre-check.
|
|
18
|
+
*
|
|
19
|
+
* The vendored Playwright fork's DataSafetyGuard/parseDataSafetyConfig read
|
|
20
|
+
* their config with precedence `options ?? env ?? inert`. Testbot resolves
|
|
21
|
+
* the full precedence chain (action input > workspace > auto-default) itself
|
|
22
|
+
* and ships the result via the SKYRAMP_DATA_SAFETY env var, which the fork
|
|
23
|
+
* reads directly. If mcp ALSO passed options derived from the workspace file
|
|
24
|
+
* in that lane, options would win over env and silently override testbot's
|
|
25
|
+
* resolution. So `resolveDataSafetyOptions` returns undefined whenever
|
|
26
|
+
* SKYRAMP_DATA_SAFETY is set — only the env-less lane (local/manual MCP
|
|
27
|
+
* usage, no testbot in the loop) reads the workspace section itself.
|
|
28
|
+
*
|
|
29
|
+
* The dataSafety section is read out of the raw YAML rather than through the
|
|
30
|
+
* full strict workspace schema (see workspace/workspace.ts), so an old or
|
|
31
|
+
* unrelated workspace.yml that fails full schema validation for reasons that
|
|
32
|
+
* have nothing to do with data safety cannot flip anyone to strict by
|
|
33
|
+
* accident. A PRESENT dataSafety section goes to the fork as written, and the
|
|
34
|
+
* fork's validateConfig checks each field on its own: a bad mode fails closed
|
|
35
|
+
* to strict, and a bad offLimits entry stops every tool call, so a bad field
|
|
36
|
+
* never drops the fields next to it. A workspace.yml that does not parse but
|
|
37
|
+
* has a top-level dataSafety key fails closed to strict.
|
|
38
|
+
*/
|
|
39
|
+
import fs from "fs";
|
|
40
|
+
import path from "path";
|
|
41
|
+
import { createRequire } from "module";
|
|
42
|
+
import yaml from "js-yaml";
|
|
43
|
+
import { logger } from "../utils/logger.js";
|
|
44
|
+
const require = createRequire(import.meta.url);
|
|
45
|
+
const TOP_LEVEL_DATA_SAFETY_KEY = /^["']?dataSafety["']?\s*:/m;
|
|
46
|
+
/**
|
|
47
|
+
* Resolve mcp-side options for the trace backend. Returns undefined when
|
|
48
|
+
* SKYRAMP_DATA_SAFETY is set (testbot's resolution wins — see module
|
|
49
|
+
* docstring) or when no dataSafety signal exists in the workspace file.
|
|
50
|
+
* Only `mode` (absent means "off") and `offLimits` are passed on, so a field
|
|
51
|
+
* added to the section later cannot change what the fork enforces.
|
|
52
|
+
*/
|
|
53
|
+
export async function resolveDataSafetyOptions(workspaceRoot) {
|
|
54
|
+
if (process.env.SKYRAMP_DATA_SAFETY?.trim())
|
|
55
|
+
return undefined;
|
|
56
|
+
const configPath = path.join(workspaceRoot, ".skyramp", "workspace.yml");
|
|
57
|
+
let text;
|
|
58
|
+
try {
|
|
59
|
+
text = fs.readFileSync(configPath, "utf8");
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return undefined; // no file: no signal
|
|
63
|
+
}
|
|
64
|
+
let raw;
|
|
65
|
+
try {
|
|
66
|
+
raw = yaml.load(text);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (!TOP_LEVEL_DATA_SAFETY_KEY.test(text))
|
|
70
|
+
return undefined;
|
|
71
|
+
logger.warning("workspace.yml is not valid YAML and has a dataSafety section — failing closed to strict, with no offLimits", { error: error instanceof Error ? error.message : String(error) });
|
|
72
|
+
return { mode: "strict" };
|
|
73
|
+
}
|
|
74
|
+
const section = raw?.["dataSafety"];
|
|
75
|
+
if (section === undefined || section === null)
|
|
76
|
+
return undefined;
|
|
77
|
+
if (typeof section !== "object" || Array.isArray(section))
|
|
78
|
+
return section;
|
|
79
|
+
const { mode, offLimits } = section;
|
|
80
|
+
return { mode: mode === undefined ? "off" : mode, offLimits };
|
|
81
|
+
}
|
|
82
|
+
/** The config exactly as the fork's parseDataSafetyConfig will resolve it. */
|
|
83
|
+
export async function effectiveDataSafetyConfig(workspaceRoot) {
|
|
84
|
+
const options = await resolveDataSafetyOptions(workspaceRoot);
|
|
85
|
+
const { parseDataSafetyConfig } = require("playwright/lib/mcp/skyramp");
|
|
86
|
+
return parseDataSafetyConfig(options, process.env.SKYRAMP_DATA_SAFETY);
|
|
87
|
+
}
|
|
88
|
+
/** offLimits check via the fork's own pure matcher so mcp and fork can never disagree. */
|
|
89
|
+
export function isOffLimitsUrl(url, config) {
|
|
90
|
+
if (!config.offLimits.length)
|
|
91
|
+
return false;
|
|
92
|
+
const { matchesOffLimits } = require("playwright/lib/mcp/skyramp");
|
|
93
|
+
return matchesOffLimits(url, config.offLimits);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Maps a resolved data-safety config to the `options` argument
|
|
97
|
+
* `registerPlaywrightTools` expects, for the enforcement-lane wiring in
|
|
98
|
+
* index.ts: `resolveDataSafetyOptions(cwd).then(ds => registerPlaywrightTools(server, playwrightToolOptionsFor(ds)))`.
|
|
99
|
+
* Extracted (rather than an inline arrow at the call site) so the mapping is
|
|
100
|
+
* directly unit-testable without booting the McpServer — mirrors
|
|
101
|
+
* `resolveDataSafetyForRegistration`'s existing test approach
|
|
102
|
+
* (registerPlaywrightTools.dataSafety.test.ts). The return type is left
|
|
103
|
+
* structural (not `PlaywrightTraceOptions`) to avoid a circular import with
|
|
104
|
+
* PlaywrightTraceService.ts, which itself imports `DataSafetyConfig` from
|
|
105
|
+
* this module.
|
|
106
|
+
*/
|
|
107
|
+
export function playwrightToolOptionsFor(dataSafety) {
|
|
108
|
+
return dataSafety !== undefined ? { dataSafety } : undefined;
|
|
109
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ESSENTIAL_TOOLS: ReadonlySet<string>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Only expose essential browser tools to reduce LLM confusion.
|
|
2
|
+
// The full set (23 tools) overwhelms the LLM and causes it to use
|
|
3
|
+
// browser_evaluate/browser_run_code instead of the simpler tools.
|
|
4
|
+
//
|
|
5
|
+
// browser_blueprint is the only DOM Analyzer tool registered — it handles
|
|
6
|
+
// both full captures and same-URL deltas via the URL-cache (isFullCapture).
|
|
7
|
+
// browser_blueprint_diff, browser_sitemap_build, browser_sitemap_query fire
|
|
8
|
+
// 0 times in production evals and have no active call path in the testbot prompt.
|
|
9
|
+
//
|
|
10
|
+
// browser_widget_contract_lookup / browser_widget_contract_cache are
|
|
11
|
+
// intentionally excluded: the curated fingerprint library only covers
|
|
12
|
+
// test fixtures (not production widgets), and the LLM can handle custom
|
|
13
|
+
// widgets via snapshot-driven trial-and-error without the tool.
|
|
14
|
+
// The widgetType field on blueprint elements still signals "custom" to
|
|
15
|
+
// the agent — no guidance is lost.
|
|
16
|
+
export const ESSENTIAL_TOOLS = new Set([
|
|
17
|
+
"browser_navigate",
|
|
18
|
+
"browser_snapshot",
|
|
19
|
+
"browser_click",
|
|
20
|
+
"browser_type",
|
|
21
|
+
"browser_press_key",
|
|
22
|
+
"browser_select_option",
|
|
23
|
+
"browser_hover",
|
|
24
|
+
"browser_drag",
|
|
25
|
+
"browser_file_upload",
|
|
26
|
+
"browser_evaluate",
|
|
27
|
+
"browser_tabs",
|
|
28
|
+
"browser_navigate_back",
|
|
29
|
+
"browser_wait_for",
|
|
30
|
+
"browser_take_screenshot",
|
|
31
|
+
"browser_assert",
|
|
32
|
+
"browser_assert_hidden",
|
|
33
|
+
"browser_assert_api_request",
|
|
34
|
+
"browser_assert_table_cell",
|
|
35
|
+
"browser_mouse_action",
|
|
36
|
+
"browser_visual_snapshot",
|
|
37
|
+
"browser_gojs_add_node",
|
|
38
|
+
"browser_gojs_link",
|
|
39
|
+
"skyramp_export_zip",
|
|
40
|
+
"skyramp_load_trace",
|
|
41
|
+
"skyramp_finish_test_setup",
|
|
42
|
+
// DOM Analyzer: blueprint capture (capture-act-capture pattern)
|
|
43
|
+
"browser_blueprint",
|
|
44
|
+
]);
|
|
@@ -3,6 +3,41 @@
|
|
|
3
3
|
* tools in the Skyramp MCP server, delegating calls to PlaywrightTraceService.
|
|
4
4
|
*/
|
|
5
5
|
import { PlaywrightTraceService, type PlaywrightTraceOptions } from "./PlaywrightTraceService.js";
|
|
6
|
-
import type {
|
|
6
|
+
import type { DataSafetyConfig } from "./dataSafetyConfig.js";
|
|
7
|
+
import type { McpServer, CallToolResult } from "@modelcontextprotocol/server";
|
|
7
8
|
export declare function getPlaywrightTraceService(): PlaywrightTraceService | null;
|
|
9
|
+
/** Courtesy pre-check so the agent gets an immediate, readable refusal
|
|
10
|
+
* instead of a page-level network error. The fork's network layer remains
|
|
11
|
+
* the hard guarantee — this is UX, not enforcement — so a throw from the
|
|
12
|
+
* matcher (e.g. the vendored fork's require() resolving a partial install)
|
|
13
|
+
* must degrade to "not off-limits" rather than block navigation outright. */
|
|
14
|
+
export declare function precheckNavigate(url: string, config: DataSafetyConfig): string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Pure regression guard for the per-call browser_navigate pre-check: the
|
|
17
|
+
* `tool.name === "browser_navigate" && config` gate, the params.url
|
|
18
|
+
* extraction, and the isError result shape, all extracted out of the tool
|
|
19
|
+
* handler closure so they can be exercised directly under Jest without a
|
|
20
|
+
* real McpServer/PlaywrightTraceService. The registration call site becomes
|
|
21
|
+
* `const refusal = maybeRefuseNavigate(...); if (refusal) return refusal;`.
|
|
22
|
+
* Returns undefined for every "let the call through" case: wrong tool, no
|
|
23
|
+
* url param, or no config (data safety not configured for this run).
|
|
24
|
+
*/
|
|
25
|
+
export declare function maybeRefuseNavigate(toolName: string, params: unknown, config: DataSafetyConfig | undefined): CallToolResult | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Resolves the data-safety pre-check config for `registerPlaywrightTools`
|
|
28
|
+
* and normalizes an inert result (mode "off" with
|
|
29
|
+
* no offLimits) to `null`. MUST go through `safeResolveDataSafety` — this
|
|
30
|
+
* runs after `_service.initialize()` has already succeeded, so an unguarded
|
|
31
|
+
* rejection here (e.g. the vendored fork's `require()` resolving a partial
|
|
32
|
+
* install) would abort registration of all 23 Playwright browser tools, not
|
|
33
|
+
* just the advisory pre-check. The inner try/catch is defense in depth in
|
|
34
|
+
* case `safeResolveDataSafety` itself ever regresses its no-throw contract —
|
|
35
|
+
* either way, an undefined/failed resolution degrades to "pre-check inert"
|
|
36
|
+
* (`null`), never a thrown error.
|
|
37
|
+
*
|
|
38
|
+
* Exported (rather than inlined) so it can be exercised directly under Jest
|
|
39
|
+
* without driving the McpServer-dependent registration flow — see
|
|
40
|
+
* `registerPlaywrightTools.dataSafety.test.ts`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveDataSafetyForRegistration(cwd: string): Promise<DataSafetyConfig | null>;
|
|
8
43
|
export declare function registerPlaywrightTools(server: McpServer, options?: PlaywrightTraceOptions): Promise<void>;
|