retestkit 1.4.1
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/.claude/commands/openspec/apply.md +23 -0
- package/.claude/commands/openspec/archive.md +27 -0
- package/.claude/commands/openspec/proposal.md +28 -0
- package/.gemini/commands/openspec/apply.toml +21 -0
- package/.gemini/commands/openspec/archive.toml +25 -0
- package/.gemini/commands/openspec/proposal.toml +26 -0
- package/.github/prompts/openspec-apply.prompt.md +22 -0
- package/.github/prompts/openspec-archive.prompt.md +26 -0
- package/.github/prompts/openspec-proposal.prompt.md +27 -0
- package/.github/workflows/release.yml +33 -0
- package/.kilocode/workflows/openspec-apply.md +17 -0
- package/.kilocode/workflows/openspec-archive.md +21 -0
- package/.kilocode/workflows/openspec-proposal.md +22 -0
- package/.mcp.json +23 -0
- package/.opencode/command/openspec-apply.md +25 -0
- package/.opencode/command/openspec-archive.md +28 -0
- package/.opencode/command/openspec-proposal.md +30 -0
- package/.roo/commands/openspec-apply.md +20 -0
- package/.roo/commands/openspec-archive.md +24 -0
- package/.roo/commands/openspec-proposal.md +25 -0
- package/.vscode/mcp.json +23 -0
- package/AGENTS.md +18 -0
- package/CLAUDE.md +18 -0
- package/LICENSE +65 -0
- package/README.md +303 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +27 -0
- package/dist/config.js.map +1 -0
- package/dist/elicitation/index.d.ts +17 -0
- package/dist/elicitation/index.d.ts.map +1 -0
- package/dist/elicitation/index.js +118 -0
- package/dist/elicitation/index.js.map +1 -0
- package/dist/elicitation/types.d.ts +35 -0
- package/dist/elicitation/types.d.ts.map +1 -0
- package/dist/elicitation/types.js +39 -0
- package/dist/elicitation/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/index.d.ts +31 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +61 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/logger.d.ts +21 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +182 -0
- package/dist/logger.js.map +1 -0
- package/dist/playwright-client/index.d.ts +29 -0
- package/dist/playwright-client/index.d.ts.map +1 -0
- package/dist/playwright-client/index.js +288 -0
- package/dist/playwright-client/index.js.map +1 -0
- package/dist/playwright-client/types.d.ts +44 -0
- package/dist/playwright-client/types.d.ts.map +1 -0
- package/dist/playwright-client/types.js +49 -0
- package/dist/playwright-client/types.js.map +1 -0
- package/dist/progress/index.d.ts +39 -0
- package/dist/progress/index.d.ts.map +1 -0
- package/dist/progress/index.js +106 -0
- package/dist/progress/index.js.map +1 -0
- package/dist/progress/types.d.ts +24 -0
- package/dist/progress/types.d.ts.map +1 -0
- package/dist/progress/types.js +2 -0
- package/dist/progress/types.js.map +1 -0
- package/dist/prompts/index.d.ts +19 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +207 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/loader.d.ts +20 -0
- package/dist/prompts/loader.d.ts.map +1 -0
- package/dist/prompts/loader.js +47 -0
- package/dist/prompts/loader.js.map +1 -0
- package/dist/resources/index.d.ts +27 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +186 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/subscriptions.d.ts +10 -0
- package/dist/resources/subscriptions.d.ts.map +1 -0
- package/dist/resources/subscriptions.js +23 -0
- package/dist/resources/subscriptions.js.map +1 -0
- package/dist/sampling/index.d.ts +11 -0
- package/dist/sampling/index.d.ts.map +1 -0
- package/dist/sampling/index.js +201 -0
- package/dist/sampling/index.js.map +1 -0
- package/dist/sampling/prompts.d.ts +56 -0
- package/dist/sampling/prompts.d.ts.map +1 -0
- package/dist/sampling/prompts.js +124 -0
- package/dist/sampling/prompts.js.map +1 -0
- package/dist/sampling/types.d.ts +57 -0
- package/dist/sampling/types.d.ts.map +1 -0
- package/dist/sampling/types.js +2 -0
- package/dist/sampling/types.js.map +1 -0
- package/dist/schemas/config.d.ts +40 -0
- package/dist/schemas/config.d.ts.map +1 -0
- package/dist/schemas/config.js +30 -0
- package/dist/schemas/config.js.map +1 -0
- package/dist/security/index.d.ts +38 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +281 -0
- package/dist/security/index.js.map +1 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +142 -0
- package/dist/server.js.map +1 -0
- package/dist/test-utils/index.d.ts +6 -0
- package/dist/test-utils/index.d.ts.map +1 -0
- package/dist/test-utils/index.js +6 -0
- package/dist/test-utils/index.js.map +1 -0
- package/dist/test-utils/mock-context.d.ts +64 -0
- package/dist/test-utils/mock-context.d.ts.map +1 -0
- package/dist/test-utils/mock-context.js +347 -0
- package/dist/test-utils/mock-context.js.map +1 -0
- package/dist/test-utils/mock-playwright-client.d.ts +62 -0
- package/dist/test-utils/mock-playwright-client.d.ts.map +1 -0
- package/dist/test-utils/mock-playwright-client.js +315 -0
- package/dist/test-utils/mock-playwright-client.js.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +8 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/webtest/crawl.d.ts +46 -0
- package/dist/tools/webtest/crawl.d.ts.map +1 -0
- package/dist/tools/webtest/crawl.js +678 -0
- package/dist/tools/webtest/crawl.js.map +1 -0
- package/dist/tools/webtest/discover-features.d.ts +30 -0
- package/dist/tools/webtest/discover-features.d.ts.map +1 -0
- package/dist/tools/webtest/discover-features.js +343 -0
- package/dist/tools/webtest/discover-features.js.map +1 -0
- package/dist/tools/webtest/discover-flows.d.ts +29 -0
- package/dist/tools/webtest/discover-flows.d.ts.map +1 -0
- package/dist/tools/webtest/discover-flows.js +341 -0
- package/dist/tools/webtest/discover-flows.js.map +1 -0
- package/dist/tools/webtest/generate-tests.d.ts +54 -0
- package/dist/tools/webtest/generate-tests.d.ts.map +1 -0
- package/dist/tools/webtest/generate-tests.js +364 -0
- package/dist/tools/webtest/generate-tests.js.map +1 -0
- package/dist/tools/webtest/index.d.ts +8 -0
- package/dist/tools/webtest/index.d.ts.map +1 -0
- package/dist/tools/webtest/index.js +8 -0
- package/dist/tools/webtest/index.js.map +1 -0
- package/dist/tools/webtest/run-test-case.d.ts +28 -0
- package/dist/tools/webtest/run-test-case.d.ts.map +1 -0
- package/dist/tools/webtest/run-test-case.js +420 -0
- package/dist/tools/webtest/run-test-case.js.map +1 -0
- package/dist/tools/webtest/schemas.d.ts +175 -0
- package/dist/tools/webtest/schemas.d.ts.map +1 -0
- package/dist/tools/webtest/schemas.js +156 -0
- package/dist/tools/webtest/schemas.js.map +1 -0
- package/dist/tools/webtest/start-analysis.d.ts +16 -0
- package/dist/tools/webtest/start-analysis.d.ts.map +1 -0
- package/dist/tools/webtest/start-analysis.js +137 -0
- package/dist/tools/webtest/start-analysis.js.map +1 -0
- package/dist/transports/http.d.ts +8 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +9 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/index.d.ts +14 -0
- package/dist/transports/index.d.ts.map +1 -0
- package/dist/transports/index.js +20 -0
- package/dist/transports/index.js.map +1 -0
- package/dist/transports/stdio.d.ts +4 -0
- package/dist/transports/stdio.d.ts.map +1 -0
- package/dist/transports/stdio.js +6 -0
- package/dist/transports/stdio.js.map +1 -0
- package/dist/types/capabilities.d.ts +18 -0
- package/dist/types/capabilities.d.ts.map +1 -0
- package/dist/types/capabilities.js +35 -0
- package/dist/types/capabilities.js.map +1 -0
- package/dist/types/context.d.ts +20 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +2 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/tool.d.ts +10 -0
- package/dist/types/tool.d.ts.map +1 -0
- package/dist/types/tool.js +2 -0
- package/dist/types/tool.js.map +1 -0
- package/dist/workspace/index.d.ts +99 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +648 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/markdown.d.ts +50 -0
- package/dist/workspace/markdown.d.ts.map +1 -0
- package/dist/workspace/markdown.js +210 -0
- package/dist/workspace/markdown.js.map +1 -0
- package/dist/workspace/types.d.ts +173 -0
- package/dist/workspace/types.d.ts.map +1 -0
- package/dist/workspace/types.js +2 -0
- package/dist/workspace/types.js.map +1 -0
- package/openspec/AGENTS.md +456 -0
- package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/proposal.md +33 -0
- package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/specs/webtest-resources/spec.md +27 -0
- package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/specs/webtest-tools/spec.md +304 -0
- package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/tasks.md +43 -0
- package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/design.md +209 -0
- package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/proposal.md +41 -0
- package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/specs/mcp-server-core/spec.md +183 -0
- package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/tasks.md +112 -0
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/design.md +333 -0
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/proposal.md +66 -0
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/mcp-server-core/spec.md +129 -0
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-lifecycle/spec.md +138 -0
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-logging/spec.md +211 -0
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-prompts/spec.md +157 -0
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-resources/spec.md +213 -0
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-sampling/spec.md +257 -0
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-tools/spec.md +501 -0
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/tasks.md +264 -0
- package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/proposal.md +24 -0
- package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/specs/webtest-tools/spec.md +80 -0
- package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/tasks.md +8 -0
- package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/design.md +90 -0
- package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/proposal.md +28 -0
- package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/specs/webtest-sampling/spec.md +90 -0
- package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/tasks.md +33 -0
- package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/design.md +558 -0
- package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/proposal.md +119 -0
- package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/specs/webtest-resources/spec.md +109 -0
- package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/specs/webtest-tools/spec.md +121 -0
- package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/tasks.md +133 -0
- package/openspec/changes/extract-prompts-to-markdown/design.md +86 -0
- package/openspec/changes/extract-prompts-to-markdown/proposal.md +50 -0
- package/openspec/changes/extract-prompts-to-markdown/specs/webtest-prompts/spec.md +74 -0
- package/openspec/changes/extract-prompts-to-markdown/tasks.md +40 -0
- package/openspec/changes/refactor-webtest-naming/design.md +95 -0
- package/openspec/changes/refactor-webtest-naming/proposal.md +66 -0
- package/openspec/changes/refactor-webtest-naming/specs/webtest-prompts/spec.md +79 -0
- package/openspec/changes/refactor-webtest-naming/specs/webtest-resources/spec.md +80 -0
- package/openspec/changes/refactor-webtest-naming/specs/webtest-sampling/spec.md +122 -0
- package/openspec/changes/refactor-webtest-naming/specs/webtest-tools/spec.md +113 -0
- package/openspec/changes/refactor-webtest-naming/tasks.md +119 -0
- package/openspec/changes/rename-package-to-retest/proposal.md +52 -0
- package/openspec/changes/rename-package-to-retest/specs/mcp-server-core/spec.md +53 -0
- package/openspec/changes/rename-package-to-retest/specs/retest-lifecycle/spec.md +68 -0
- package/openspec/changes/rename-package-to-retest/specs/retest-logging/spec.md +35 -0
- package/openspec/changes/rename-package-to-retest/specs/retest-prompts/spec.md +159 -0
- package/openspec/changes/rename-package-to-retest/specs/retest-resources/spec.md +251 -0
- package/openspec/changes/rename-package-to-retest/specs/retest-sampling/spec.md +99 -0
- package/openspec/changes/rename-package-to-retest/specs/retest-tools/spec.md +295 -0
- package/openspec/changes/rename-package-to-retest/tasks.md +71 -0
- package/openspec/project.md +31 -0
- package/openspec/specs/mcp-server-core/spec.md +178 -0
- package/openspec/specs/webtest-lifecycle/spec.md +136 -0
- package/openspec/specs/webtest-logging/spec.md +209 -0
- package/openspec/specs/webtest-prompts/spec.md +155 -0
- package/openspec/specs/webtest-resources/spec.md +248 -0
- package/openspec/specs/webtest-sampling/spec.md +344 -0
- package/openspec/specs/webtest-tools/spec.md +282 -0
- package/package.json +54 -0
- package/release.config.js +9 -0
- package/src/config.test.ts +96 -0
- package/src/config.ts +32 -0
- package/src/elicitation/index.test.ts +399 -0
- package/src/elicitation/index.ts +171 -0
- package/src/elicitation/types.ts +68 -0
- package/src/index.ts +83 -0
- package/src/lifecycle/index.test.ts +260 -0
- package/src/lifecycle/index.ts +101 -0
- package/src/logger.redaction.test.ts +322 -0
- package/src/logger.test.ts +123 -0
- package/src/logger.ts +229 -0
- package/src/playwright-client/index.ts +392 -0
- package/src/playwright-client/types.ts +99 -0
- package/src/progress/index.test.ts +327 -0
- package/src/progress/index.ts +170 -0
- package/src/progress/types.ts +25 -0
- package/src/prompts/index.test.ts +451 -0
- package/src/prompts/index.ts +246 -0
- package/src/prompts/loader.test.ts +100 -0
- package/src/prompts/loader.ts +59 -0
- package/src/prompts/templates/mcp/webtest-crawl.md +7 -0
- package/src/prompts/templates/mcp/webtest-discover-flows.md +11 -0
- package/src/prompts/templates/mcp/webtest-discover.md +12 -0
- package/src/prompts/templates/mcp/webtest-full-workflow.md +12 -0
- package/src/prompts/templates/mcp/webtest-generate-tests.md +11 -0
- package/src/prompts/templates/mcp/webtest-run-test.md +11 -0
- package/src/prompts/templates/mcp/webtest-start.md +8 -0
- package/src/prompts/templates/sampling/crawl-action.md +35 -0
- package/src/prompts/templates/sampling/feature-discovery.md +27 -0
- package/src/prompts/templates/sampling/flow-discovery.md +29 -0
- package/src/prompts/templates/sampling/page-content-wrapper.md +5 -0
- package/src/prompts/templates/sampling/system-prefix.md +12 -0
- package/src/prompts/templates/sampling/test-evaluation.md +17 -0
- package/src/prompts/templates/sampling/test-generation.md +31 -0
- package/src/resources/index.ts +250 -0
- package/src/resources/subscriptions.ts +37 -0
- package/src/sampling/index.test.ts +414 -0
- package/src/sampling/index.ts +286 -0
- package/src/sampling/prompts.ts +194 -0
- package/src/sampling/types.ts +60 -0
- package/src/schemas/config.ts +39 -0
- package/src/security/index.test.ts +441 -0
- package/src/security/index.ts +361 -0
- package/src/security/security-scenarios.test.ts +468 -0
- package/src/server.ts +211 -0
- package/src/test-utils/index.ts +6 -0
- package/src/test-utils/mock-context.ts +426 -0
- package/src/test-utils/mock-playwright-client.ts +422 -0
- package/src/tools/index.ts +11 -0
- package/src/tools/webtest/crawl.test.ts +834 -0
- package/src/tools/webtest/crawl.ts +901 -0
- package/src/tools/webtest/discover-features.ts +412 -0
- package/src/tools/webtest/discover-flows.ts +408 -0
- package/src/tools/webtest/generate-tests.test.ts +532 -0
- package/src/tools/webtest/generate-tests.ts +425 -0
- package/src/tools/webtest/index.ts +7 -0
- package/src/tools/webtest/integration.test.ts +536 -0
- package/src/tools/webtest/run-test-case.test.ts +659 -0
- package/src/tools/webtest/run-test-case.ts +508 -0
- package/src/tools/webtest/schemas.ts +201 -0
- package/src/tools/webtest/start-analysis.test.ts +151 -0
- package/src/tools/webtest/start-analysis.ts +158 -0
- package/src/transports/http.ts +19 -0
- package/src/transports/index.ts +30 -0
- package/src/transports/stdio.ts +7 -0
- package/src/types/capabilities.test.ts +193 -0
- package/src/types/capabilities.ts +50 -0
- package/src/types/context.ts +21 -0
- package/src/types/tool.ts +11 -0
- package/src/workspace/index.ts +945 -0
- package/src/workspace/markdown.ts +272 -0
- package/src/workspace/types.ts +186 -0
- package/tests/integration/server.test.ts +89 -0
- package/tests/integration/tools.test.ts +99 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +9 -0
- package/vitest.integration.config.ts +10 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
2
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
4
|
+
import type { Logger } from "../logger.js";
|
|
5
|
+
import type { Config } from "../schemas/config.js";
|
|
6
|
+
import {
|
|
7
|
+
type PlaywrightToolMapping,
|
|
8
|
+
type PlaywrightToolCallResult,
|
|
9
|
+
type PlaywrightSnapshot,
|
|
10
|
+
type PlaywrightScreenshot,
|
|
11
|
+
CANONICAL_TOOL_NAMES,
|
|
12
|
+
TOOL_NAME_VARIANTS,
|
|
13
|
+
REQUIRED_TOOLS,
|
|
14
|
+
} from "./types.js";
|
|
15
|
+
|
|
16
|
+
export type { PlaywrightToolMapping, PlaywrightSnapshot, PlaywrightScreenshot };
|
|
17
|
+
|
|
18
|
+
export interface TypeOptions {
|
|
19
|
+
submit?: boolean;
|
|
20
|
+
slowly?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface PlaywrightClient {
|
|
24
|
+
connect(): Promise<void>;
|
|
25
|
+
disconnect(): Promise<void>;
|
|
26
|
+
isConnected(): boolean;
|
|
27
|
+
getToolMapping(): PlaywrightToolMapping | null;
|
|
28
|
+
callTool(
|
|
29
|
+
canonicalName: keyof PlaywrightToolMapping,
|
|
30
|
+
args: Record<string, unknown>
|
|
31
|
+
): Promise<PlaywrightToolCallResult>;
|
|
32
|
+
snapshot(): Promise<PlaywrightSnapshot>;
|
|
33
|
+
screenshot(): Promise<PlaywrightScreenshot>;
|
|
34
|
+
navigate(url: string): Promise<void>;
|
|
35
|
+
click(element: string, ref: string): Promise<void>;
|
|
36
|
+
type(element: string, ref: string, text: string, options?: TypeOptions): Promise<void>;
|
|
37
|
+
fill(element: string, ref: string, value: string): Promise<void>;
|
|
38
|
+
hover(element: string, ref: string): Promise<void>;
|
|
39
|
+
select(element: string, ref: string, values: string[]): Promise<void>;
|
|
40
|
+
evaluate(fn: string, element?: string, ref?: string): Promise<unknown>;
|
|
41
|
+
press(key: string): Promise<void>;
|
|
42
|
+
scroll(x: number, y: number): Promise<void>;
|
|
43
|
+
wait(ms: number): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function createPlaywrightClient(
|
|
47
|
+
config: Config,
|
|
48
|
+
logger: Logger
|
|
49
|
+
): PlaywrightClient {
|
|
50
|
+
let client: Client | null = null;
|
|
51
|
+
let transport: StdioClientTransport | null = null;
|
|
52
|
+
let process: ChildProcess | null = null;
|
|
53
|
+
let toolMapping: PlaywrightToolMapping | null = null;
|
|
54
|
+
let connected = false;
|
|
55
|
+
|
|
56
|
+
async function discoverTools(): Promise<PlaywrightToolMapping> {
|
|
57
|
+
if (!client) {
|
|
58
|
+
throw new Error("Client not connected");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const response = await client.listTools();
|
|
62
|
+
const availableTools = new Set(response.tools.map((t) => t.name));
|
|
63
|
+
const mapping: Partial<PlaywrightToolMapping> = {};
|
|
64
|
+
|
|
65
|
+
logger.debug("Discovered Playwright MCP tools", {
|
|
66
|
+
count: response.tools.length,
|
|
67
|
+
tools: response.tools.map((t) => t.name),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
for (const canonical of CANONICAL_TOOL_NAMES) {
|
|
71
|
+
const variants = TOOL_NAME_VARIANTS[canonical];
|
|
72
|
+
const found = variants.find((v) => availableTools.has(v));
|
|
73
|
+
if (found) {
|
|
74
|
+
mapping[canonical] = found;
|
|
75
|
+
logger.debug("Mapped tool", { canonical, actual: found });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Check for required tools
|
|
80
|
+
const missingRequired = REQUIRED_TOOLS.filter((t) => !mapping[t]);
|
|
81
|
+
if (missingRequired.length > 0) {
|
|
82
|
+
logger.warn("Missing required Playwright tools", {
|
|
83
|
+
missing: missingRequired,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Determine implementation variant
|
|
88
|
+
const sampleTool = Object.values(mapping)[0] || "";
|
|
89
|
+
let variant = "unknown";
|
|
90
|
+
if (sampleTool.startsWith("browser_")) {
|
|
91
|
+
variant = "microsoft";
|
|
92
|
+
} else if (sampleTool.startsWith("playwright_")) {
|
|
93
|
+
variant = "anthropic";
|
|
94
|
+
} else {
|
|
95
|
+
variant = "generic";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
logger.info("Playwright MCP implementation detected", {
|
|
99
|
+
variant,
|
|
100
|
+
mappedTools: Object.keys(mapping).length,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return mapping as PlaywrightToolMapping;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function callToolInternal(
|
|
107
|
+
toolName: string,
|
|
108
|
+
args: Record<string, unknown>
|
|
109
|
+
): Promise<PlaywrightToolCallResult> {
|
|
110
|
+
if (!client) {
|
|
111
|
+
throw new Error("Client not connected");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
logger.debug("Calling Playwright tool", { tool: toolName, args });
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const result = await client.callTool({ name: toolName, arguments: args });
|
|
118
|
+
return result as PlaywrightToolCallResult;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
121
|
+
logger.error("Playwright tool call failed", {
|
|
122
|
+
tool: toolName,
|
|
123
|
+
error: message,
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
127
|
+
isError: true,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
async connect(): Promise<void> {
|
|
134
|
+
if (connected) {
|
|
135
|
+
logger.warn("Playwright client already connected");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
logger.info("Connecting to Playwright MCP server", {
|
|
140
|
+
command: config.playwrightMcpCommand,
|
|
141
|
+
args: config.playwrightMcpArgs,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
transport = new StdioClientTransport({
|
|
145
|
+
command: config.playwrightMcpCommand,
|
|
146
|
+
args: config.playwrightMcpArgs,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
client = new Client(
|
|
150
|
+
{ name: "testing-mcp", version: "0.1.0" },
|
|
151
|
+
{ capabilities: {} }
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
await client.connect(transport);
|
|
155
|
+
connected = true;
|
|
156
|
+
|
|
157
|
+
toolMapping = await discoverTools();
|
|
158
|
+
|
|
159
|
+
logger.info("Playwright MCP client connected");
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
async disconnect(): Promise<void> {
|
|
163
|
+
if (!connected) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
logger.info("Disconnecting from Playwright MCP server");
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
await client?.close();
|
|
171
|
+
} catch (error) {
|
|
172
|
+
logger.warn("Error closing client", {
|
|
173
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (process) {
|
|
178
|
+
process.kill();
|
|
179
|
+
process = null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
client = null;
|
|
183
|
+
transport = null;
|
|
184
|
+
toolMapping = null;
|
|
185
|
+
connected = false;
|
|
186
|
+
|
|
187
|
+
logger.info("Playwright MCP client disconnected");
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
isConnected(): boolean {
|
|
191
|
+
return connected;
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
getToolMapping(): PlaywrightToolMapping | null {
|
|
195
|
+
return toolMapping;
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
async callTool(
|
|
199
|
+
canonicalName: keyof PlaywrightToolMapping,
|
|
200
|
+
args: Record<string, unknown>
|
|
201
|
+
): Promise<PlaywrightToolCallResult> {
|
|
202
|
+
if (!toolMapping) {
|
|
203
|
+
throw new Error("Tool mapping not initialized");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const actualName = toolMapping[canonicalName];
|
|
207
|
+
if (!actualName) {
|
|
208
|
+
throw new Error(`Tool ${canonicalName} not available`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return callToolInternal(actualName, args);
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
async snapshot(): Promise<PlaywrightSnapshot> {
|
|
215
|
+
const result = await this.callTool("snapshot", {});
|
|
216
|
+
|
|
217
|
+
if (result.isError) {
|
|
218
|
+
throw new Error(result.content[0]?.text || "Snapshot failed");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const text = result.content.find((c) => c.type === "text")?.text || "";
|
|
222
|
+
// Parse the snapshot response - format varies by implementation
|
|
223
|
+
try {
|
|
224
|
+
const parsed = JSON.parse(text);
|
|
225
|
+
return {
|
|
226
|
+
url: parsed.url || "",
|
|
227
|
+
title: parsed.title || "",
|
|
228
|
+
content: parsed.content || text,
|
|
229
|
+
};
|
|
230
|
+
} catch {
|
|
231
|
+
// Microsoft Playwright MCP returns markdown format:
|
|
232
|
+
// "- Page URL: https://example.com/\n- Page Title: Example\n- Page Snapshot:\n```yaml\n..."
|
|
233
|
+
// Extract URL and title from markdown format
|
|
234
|
+
let url = "";
|
|
235
|
+
let title = "";
|
|
236
|
+
|
|
237
|
+
const urlMatch = text.match(/Page URL:\s*(\S+)/);
|
|
238
|
+
if (urlMatch) {
|
|
239
|
+
url = urlMatch[1];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const titleMatch = text.match(/Page Title:\s*(.+?)(?:\n|$)/);
|
|
243
|
+
if (titleMatch) {
|
|
244
|
+
title = titleMatch[1].trim();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
url,
|
|
249
|
+
title,
|
|
250
|
+
content: text,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
async screenshot(): Promise<PlaywrightScreenshot> {
|
|
256
|
+
const result = await this.callTool("screenshot", {});
|
|
257
|
+
|
|
258
|
+
if (result.isError) {
|
|
259
|
+
throw new Error(result.content[0]?.text || "Screenshot failed");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const imageContent = result.content.find((c) => c.type === "image");
|
|
263
|
+
if (!imageContent || !imageContent.data) {
|
|
264
|
+
throw new Error("No image data in screenshot response");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
data: imageContent.data,
|
|
269
|
+
mimeType: imageContent.mimeType || "image/png",
|
|
270
|
+
};
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
async navigate(url: string): Promise<void> {
|
|
274
|
+
const result = await this.callTool("navigate", { url });
|
|
275
|
+
|
|
276
|
+
if (result.isError) {
|
|
277
|
+
throw new Error(result.content[0]?.text || "Navigation failed");
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
async click(element: string, ref: string): Promise<void> {
|
|
282
|
+
const result = await this.callTool("click", { element, ref });
|
|
283
|
+
|
|
284
|
+
if (result.isError) {
|
|
285
|
+
throw new Error(result.content[0]?.text || "Click failed");
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
async type(element: string, ref: string, text: string, options?: { submit?: boolean; slowly?: boolean }): Promise<void> {
|
|
290
|
+
const result = await this.callTool("type", {
|
|
291
|
+
element,
|
|
292
|
+
ref,
|
|
293
|
+
text,
|
|
294
|
+
submit: options?.submit ?? false,
|
|
295
|
+
slowly: options?.slowly ?? false,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
if (result.isError) {
|
|
299
|
+
throw new Error(result.content[0]?.text || "Type failed");
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
async fill(element: string, ref: string, value: string): Promise<void> {
|
|
304
|
+
// Microsoft Playwright MCP uses browser_type for fill operations
|
|
305
|
+
// The fill operation is essentially type without slowly mode
|
|
306
|
+
const result = await this.callTool("fill", {
|
|
307
|
+
element,
|
|
308
|
+
ref,
|
|
309
|
+
text: value,
|
|
310
|
+
submit: false,
|
|
311
|
+
slowly: false,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
if (result.isError) {
|
|
315
|
+
throw new Error(result.content[0]?.text || "Fill failed");
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
async hover(element: string, ref: string): Promise<void> {
|
|
320
|
+
const result = await this.callTool("hover", { element, ref });
|
|
321
|
+
|
|
322
|
+
if (result.isError) {
|
|
323
|
+
throw new Error(result.content[0]?.text || "Hover failed");
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
async select(element: string, ref: string, values: string[]): Promise<void> {
|
|
328
|
+
const result = await this.callTool("select", { element, ref, values });
|
|
329
|
+
|
|
330
|
+
if (result.isError) {
|
|
331
|
+
throw new Error(result.content[0]?.text || "Select failed");
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
async evaluate(fn: string, element?: string, ref?: string): Promise<unknown> {
|
|
336
|
+
const args: Record<string, unknown> = { function: fn };
|
|
337
|
+
if (element) args.element = element;
|
|
338
|
+
if (ref) args.ref = ref;
|
|
339
|
+
|
|
340
|
+
const result = await this.callTool("evaluate", args);
|
|
341
|
+
|
|
342
|
+
if (result.isError) {
|
|
343
|
+
throw new Error(result.content[0]?.text || "Evaluate failed");
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const text = result.content.find((c) => c.type === "text")?.text;
|
|
347
|
+
if (text) {
|
|
348
|
+
try {
|
|
349
|
+
return JSON.parse(text);
|
|
350
|
+
} catch {
|
|
351
|
+
return text;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return undefined;
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
async press(key: string): Promise<void> {
|
|
358
|
+
// Microsoft Playwright MCP browser_press_key takes only the key, no element
|
|
359
|
+
const result = await this.callTool("press", { key });
|
|
360
|
+
|
|
361
|
+
if (result.isError) {
|
|
362
|
+
throw new Error(result.content[0]?.text || "Press failed");
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
async scroll(x: number, y: number): Promise<void> {
|
|
367
|
+
// scroll may not exist in Microsoft implementation - try direct tool first
|
|
368
|
+
// If it fails, fall back to evaluate
|
|
369
|
+
if (toolMapping?.scroll) {
|
|
370
|
+
const result = await this.callTool("scroll", { x, y });
|
|
371
|
+
if (!result.isError) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// Fallback: use evaluate to scroll
|
|
376
|
+
await this.evaluate(`() => window.scrollTo(${x}, ${y})`);
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
async wait(ms: number): Promise<void> {
|
|
380
|
+
// wait may not exist in Microsoft implementation - try direct tool first
|
|
381
|
+
// If it fails, fall back to evaluate
|
|
382
|
+
if (toolMapping?.wait) {
|
|
383
|
+
const result = await this.callTool("wait", { ms });
|
|
384
|
+
if (!result.isError) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// Fallback: use evaluate to wait
|
|
389
|
+
await this.evaluate(`() => new Promise(resolve => setTimeout(resolve, ${ms}))`);
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export interface PlaywrightToolMapping {
|
|
2
|
+
snapshot: string;
|
|
3
|
+
screenshot: string;
|
|
4
|
+
click: string;
|
|
5
|
+
type: string;
|
|
6
|
+
navigate: string;
|
|
7
|
+
fill: string;
|
|
8
|
+
hover: string;
|
|
9
|
+
select: string;
|
|
10
|
+
evaluate: string;
|
|
11
|
+
press: string;
|
|
12
|
+
scroll: string;
|
|
13
|
+
wait: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PlaywrightTool {
|
|
17
|
+
name: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
inputSchema?: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PlaywrightToolListResponse {
|
|
23
|
+
tools: PlaywrightTool[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface PlaywrightToolCallResult {
|
|
27
|
+
content: Array<{
|
|
28
|
+
type: "text" | "image";
|
|
29
|
+
text?: string;
|
|
30
|
+
data?: string;
|
|
31
|
+
mimeType?: string;
|
|
32
|
+
}>;
|
|
33
|
+
isError?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface PlaywrightSnapshot {
|
|
37
|
+
url: string;
|
|
38
|
+
title: string;
|
|
39
|
+
content: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface PlaywrightScreenshot {
|
|
43
|
+
data: string;
|
|
44
|
+
mimeType: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const CANONICAL_TOOL_NAMES: (keyof PlaywrightToolMapping)[] = [
|
|
48
|
+
"snapshot",
|
|
49
|
+
"screenshot",
|
|
50
|
+
"click",
|
|
51
|
+
"type",
|
|
52
|
+
"navigate",
|
|
53
|
+
"fill",
|
|
54
|
+
"hover",
|
|
55
|
+
"select",
|
|
56
|
+
"evaluate",
|
|
57
|
+
"press",
|
|
58
|
+
"scroll",
|
|
59
|
+
"wait",
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
export const TOOL_NAME_VARIANTS: Record<
|
|
63
|
+
keyof PlaywrightToolMapping,
|
|
64
|
+
string[]
|
|
65
|
+
> = {
|
|
66
|
+
snapshot: ["browser_snapshot", "playwright_snapshot", "snapshot"],
|
|
67
|
+
screenshot: [
|
|
68
|
+
"browser_take_screenshot",
|
|
69
|
+
"playwright_screenshot",
|
|
70
|
+
"screenshot",
|
|
71
|
+
"browser_screenshot",
|
|
72
|
+
],
|
|
73
|
+
click: ["browser_click", "playwright_click", "click"],
|
|
74
|
+
type: ["browser_type", "playwright_type", "type"],
|
|
75
|
+
navigate: ["browser_navigate", "playwright_navigate", "navigate"],
|
|
76
|
+
// Note: Microsoft Playwright MCP uses browser_type for fill operations too
|
|
77
|
+
fill: ["browser_type", "browser_fill", "playwright_fill", "fill"],
|
|
78
|
+
hover: ["browser_hover", "playwright_hover", "hover"],
|
|
79
|
+
// Microsoft Playwright MCP uses browser_select_option
|
|
80
|
+
select: ["browser_select_option", "browser_select", "playwright_select", "select"],
|
|
81
|
+
evaluate: [
|
|
82
|
+
"browser_evaluate",
|
|
83
|
+
"browser_run_code",
|
|
84
|
+
"playwright_evaluate",
|
|
85
|
+
"evaluate",
|
|
86
|
+
],
|
|
87
|
+
// Microsoft Playwright MCP uses browser_press_key
|
|
88
|
+
press: ["browser_press_key", "browser_press", "playwright_press", "press"],
|
|
89
|
+
// scroll and wait may not exist in Microsoft implementation - fallback to evaluate
|
|
90
|
+
scroll: ["browser_scroll", "playwright_scroll", "scroll"],
|
|
91
|
+
wait: ["browser_wait", "playwright_wait", "wait"],
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const REQUIRED_TOOLS: (keyof PlaywrightToolMapping)[] = [
|
|
95
|
+
"snapshot",
|
|
96
|
+
"screenshot",
|
|
97
|
+
"click",
|
|
98
|
+
"navigate",
|
|
99
|
+
];
|