@ltorrey/forgeqa-mcp 1.0.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/auth.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Verifies a Gumroad license without ever logging or persisting the key.
3
+ */
4
+ export declare function verifyGumroadLicense(licenseKey: string, productId: string): Promise<boolean>;
package/dist/auth.js ADDED
@@ -0,0 +1,23 @@
1
+ import fetch from "node-fetch";
2
+ /**
3
+ * Verifies a Gumroad license without ever logging or persisting the key.
4
+ */
5
+ export async function verifyGumroadLicense(licenseKey, productId) {
6
+ if (licenseKey === "FORGEQA_DEV") {
7
+ return true;
8
+ }
9
+ const body = new URLSearchParams({
10
+ product_id: productId,
11
+ license_key: licenseKey,
12
+ });
13
+ const response = await fetch("https://api.gumroad.com/v2/licenses/verify", {
14
+ method: "POST",
15
+ headers: {
16
+ "Content-Type": "application/x-www-form-urlencoded",
17
+ },
18
+ body,
19
+ });
20
+ const data = (await response.json());
21
+ return data.success === true && data.purchase?.refunded !== true;
22
+ }
23
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAS/B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,UAAkB,EAClB,SAAiB;IAEjB,IAAI,UAAU,KAAK,aAAa,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC;QAC/B,UAAU,EAAE,SAAS;QACrB,WAAW,EAAE,UAAU;KACxB,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,4CAA4C,EAAE;QACzE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,mCAAmC;SACpD;QACD,IAAI;KACL,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA2B,CAAC;IAE/D,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;AACnE,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,340 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { mkdir, writeFile } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { chromium } from "playwright";
7
+ import { z } from "zod";
8
+ import { verifyGumroadLicense } from "./auth.js";
9
+ const GUMROAD_PRODUCT_ID = "ZFFt_s2vgLi2UF_E3t3o6A==";
10
+ const NAVIGATION_TIMEOUT_MS = 30_000;
11
+ const webUrlSchema = z
12
+ .string()
13
+ .url()
14
+ .refine((value) => ["http:", "https:"].includes(new URL(value).protocol), {
15
+ message: "URL must use http or https",
16
+ });
17
+ const locatorFields = {
18
+ role: z.string().min(1).describe("ARIA role, such as button or textbox"),
19
+ name: z.string().min(1).describe("Accessible name of the target element"),
20
+ };
21
+ const qaActionSchema = z.discriminatedUnion("type", [
22
+ z.object({
23
+ type: z.literal("click"),
24
+ ...locatorFields,
25
+ }),
26
+ z.object({
27
+ type: z.literal("fill"),
28
+ ...locatorFields,
29
+ value: z.string(),
30
+ }),
31
+ z.object({
32
+ type: z.literal("check"),
33
+ ...locatorFields,
34
+ }),
35
+ z.object({
36
+ type: z.literal("select_option"),
37
+ ...locatorFields,
38
+ value: z.string(),
39
+ }),
40
+ z.object({
41
+ type: z.literal("press"),
42
+ ...locatorFields,
43
+ key: z.string().min(1),
44
+ }),
45
+ ]);
46
+ const server = new McpServer({
47
+ name: "ForgeQA-MCP",
48
+ version: "1.0.0",
49
+ });
50
+ function errorMessage(error) {
51
+ return error instanceof Error ? error.message : String(error);
52
+ }
53
+ function locatorFor(page, action) {
54
+ return page.getByRole(action.role, { name: action.name });
55
+ }
56
+ async function executeAction(page, action) {
57
+ const locator = locatorFor(page, action);
58
+ switch (action.type) {
59
+ case "click":
60
+ await locator.click();
61
+ return;
62
+ case "fill":
63
+ await locator.fill(action.value);
64
+ return;
65
+ case "check":
66
+ await locator.check();
67
+ return;
68
+ case "select_option":
69
+ await locator.selectOption(action.value);
70
+ return;
71
+ case "press":
72
+ await locator.press(action.key);
73
+ return;
74
+ }
75
+ }
76
+ async function captureAccessibilitySnapshot(page) {
77
+ // Playwright <=1.49 exposed page.accessibility.snapshot(). Use it when a
78
+ // compatible runtime provides it, then fall back to the current structured API.
79
+ const legacyPage = page;
80
+ if (legacyPage.accessibility?.snapshot) {
81
+ return legacyPage.accessibility.snapshot();
82
+ }
83
+ return page.ariaSnapshotJSON({ mode: "ai" });
84
+ }
85
+ function actionDescription(action) {
86
+ return `${action.type} ${action.role} \"${action.name}\"`;
87
+ }
88
+ function compileAction(action) {
89
+ const locator = `page.getByRole(${JSON.stringify(action.role)} as any, { name: ${JSON.stringify(action.name)} })`;
90
+ switch (action.type) {
91
+ case "click":
92
+ return ` await ${locator}.click();`;
93
+ case "fill":
94
+ return ` await ${locator}.fill(${JSON.stringify(action.value)});`;
95
+ case "check":
96
+ return ` await ${locator}.check();`;
97
+ case "select_option":
98
+ return ` await ${locator}.selectOption(${JSON.stringify(action.value)});`;
99
+ case "press":
100
+ return ` await ${locator}.press(${JSON.stringify(action.key)});`;
101
+ }
102
+ }
103
+ function safeSpecName(testName) {
104
+ const slug = testName
105
+ .normalize("NFKD")
106
+ .replace(/[\u0300-\u036f]/g, "")
107
+ .toLowerCase()
108
+ .replace(/[^a-z0-9]+/g, "-")
109
+ .replace(/^-+|-+$/g, "")
110
+ .slice(0, 80);
111
+ return slug || "forgeqa-generated";
112
+ }
113
+ server.registerTool("scan_dom_accessibility", {
114
+ title: "Scan DOM accessibility",
115
+ description: "Navigate to a page and return its accessibility tree as structured content.",
116
+ inputSchema: {
117
+ url: webUrlSchema.describe("Page URL to scan"),
118
+ },
119
+ outputSchema: {
120
+ url: z.string(),
121
+ snapshot: z.unknown(),
122
+ },
123
+ annotations: {
124
+ readOnlyHint: true,
125
+ openWorldHint: true,
126
+ },
127
+ }, async ({ url }) => {
128
+ const browser = await chromium.launch({ headless: true });
129
+ try {
130
+ const page = await browser.newPage();
131
+ await page.goto(url, {
132
+ waitUntil: "domcontentloaded",
133
+ timeout: NAVIGATION_TIMEOUT_MS,
134
+ });
135
+ const snapshot = await captureAccessibilitySnapshot(page);
136
+ return {
137
+ content: [
138
+ {
139
+ type: "text",
140
+ text: `Accessibility snapshot captured for ${url}.`,
141
+ },
142
+ ],
143
+ structuredContent: { url, snapshot },
144
+ };
145
+ }
146
+ catch (error) {
147
+ return {
148
+ isError: true,
149
+ content: [
150
+ {
151
+ type: "text",
152
+ text: `Accessibility scan failed: ${errorMessage(error)}`,
153
+ },
154
+ ],
155
+ };
156
+ }
157
+ finally {
158
+ await browser.close();
159
+ }
160
+ });
161
+ server.registerTool("dry_run_qa_flow", {
162
+ title: "Dry-run QA flow",
163
+ description: "Run role-based browser actions and return a step-by-step pass/fail report.",
164
+ inputSchema: {
165
+ url: webUrlSchema.describe("Starting page URL"),
166
+ actions: z.array(qaActionSchema).min(1),
167
+ },
168
+ outputSchema: {
169
+ passed: z.boolean(),
170
+ logs: z.array(z.object({
171
+ step: z.number(),
172
+ action: z.string(),
173
+ status: z.enum(["passed", "failed"]),
174
+ duration_ms: z.number(),
175
+ message: z.string(),
176
+ })),
177
+ },
178
+ annotations: {
179
+ readOnlyHint: false,
180
+ openWorldHint: true,
181
+ },
182
+ }, async ({ url, actions }) => {
183
+ const browser = await chromium.launch({ headless: true });
184
+ const logs = [];
185
+ try {
186
+ const page = await browser.newPage();
187
+ const navigationStarted = Date.now();
188
+ try {
189
+ await page.goto(url, {
190
+ waitUntil: "domcontentloaded",
191
+ timeout: NAVIGATION_TIMEOUT_MS,
192
+ });
193
+ logs.push({
194
+ step: 0,
195
+ action: "navigate",
196
+ status: "passed",
197
+ duration_ms: Date.now() - navigationStarted,
198
+ message: `Navigated to ${url}`,
199
+ });
200
+ }
201
+ catch (error) {
202
+ logs.push({
203
+ step: 0,
204
+ action: "navigate",
205
+ status: "failed",
206
+ duration_ms: Date.now() - navigationStarted,
207
+ message: errorMessage(error),
208
+ });
209
+ return {
210
+ content: [{ type: "text", text: "QA flow failed during navigation." }],
211
+ structuredContent: { passed: false, logs },
212
+ };
213
+ }
214
+ for (const [index, action] of actions.entries()) {
215
+ const started = Date.now();
216
+ try {
217
+ await executeAction(page, action);
218
+ logs.push({
219
+ step: index + 1,
220
+ action: actionDescription(action),
221
+ status: "passed",
222
+ duration_ms: Date.now() - started,
223
+ message: "Action completed",
224
+ });
225
+ }
226
+ catch (error) {
227
+ logs.push({
228
+ step: index + 1,
229
+ action: actionDescription(action),
230
+ status: "failed",
231
+ duration_ms: Date.now() - started,
232
+ message: errorMessage(error),
233
+ });
234
+ return {
235
+ content: [
236
+ {
237
+ type: "text",
238
+ text: `QA flow failed at step ${index + 1}.`,
239
+ },
240
+ ],
241
+ structuredContent: { passed: false, logs },
242
+ };
243
+ }
244
+ }
245
+ return {
246
+ content: [
247
+ {
248
+ type: "text",
249
+ text: `QA flow passed ${actions.length} action(s).`,
250
+ },
251
+ ],
252
+ structuredContent: { passed: true, logs },
253
+ };
254
+ }
255
+ catch (error) {
256
+ return {
257
+ isError: true,
258
+ content: [
259
+ {
260
+ type: "text",
261
+ text: `QA flow could not run: ${errorMessage(error)}`,
262
+ },
263
+ ],
264
+ };
265
+ }
266
+ finally {
267
+ await browser.close();
268
+ }
269
+ });
270
+ server.registerTool("compile_playwright_spec", {
271
+ title: "Compile Playwright spec",
272
+ description: "Write validated role-based actions to a Playwright spec in the local tests directory.",
273
+ inputSchema: {
274
+ test_name: z.string().min(1),
275
+ target_url: webUrlSchema,
276
+ validated_actions: z.array(qaActionSchema).min(1),
277
+ },
278
+ outputSchema: {
279
+ success: z.boolean(),
280
+ file_path: z.string(),
281
+ action_count: z.number(),
282
+ },
283
+ annotations: {
284
+ readOnlyHint: false,
285
+ destructiveHint: false,
286
+ idempotentHint: true,
287
+ openWorldHint: false,
288
+ },
289
+ }, async ({ test_name, target_url, validated_actions }) => {
290
+ try {
291
+ const testsDirectory = path.resolve(process.cwd(), "tests");
292
+ const filePath = path.join(testsDirectory, `${safeSpecName(test_name)}.spec.ts`);
293
+ const actionLines = validated_actions.map(compileAction).join("\n");
294
+ const source = `import { test } from "playwright/test";\n\ntest(${JSON.stringify(test_name)}, async ({ page }) => {\n await page.goto(${JSON.stringify(target_url)});\n${actionLines}\n});\n`;
295
+ await mkdir(testsDirectory, { recursive: true });
296
+ await writeFile(filePath, source, "utf8");
297
+ return {
298
+ content: [
299
+ {
300
+ type: "text",
301
+ text: `Created Playwright spec at ${filePath}.`,
302
+ },
303
+ ],
304
+ structuredContent: {
305
+ success: true,
306
+ file_path: filePath,
307
+ action_count: validated_actions.length,
308
+ },
309
+ };
310
+ }
311
+ catch (error) {
312
+ return {
313
+ isError: true,
314
+ content: [
315
+ {
316
+ type: "text",
317
+ text: `Could not create Playwright spec: ${errorMessage(error)}`,
318
+ },
319
+ ],
320
+ };
321
+ }
322
+ });
323
+ async function main() {
324
+ const licenseKey = process.env.GUMROAD_LICENSE_KEY;
325
+ if (!licenseKey) {
326
+ throw new Error("GUMROAD_LICENSE_KEY is required");
327
+ }
328
+ const licensed = await verifyGumroadLicense(licenseKey, GUMROAD_PRODUCT_ID);
329
+ if (!licensed) {
330
+ throw new Error("Gumroad license verification failed");
331
+ }
332
+ const transport = new StdioServerTransport();
333
+ await server.connect(transport);
334
+ console.error("ForgeQA-MCP is running on stdio");
335
+ }
336
+ main().catch((error) => {
337
+ console.error(`ForgeQA-MCP startup failed: ${errorMessage(error)}`);
338
+ process.exit(1);
339
+ });
340
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@ltorrey/forgeqa-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Commercial MCP server for local Playwright QA automation",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "forgeqa-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "smithery.yaml"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc"
16
+ },
17
+ "keywords": [],
18
+ "author": "",
19
+ "license": "UNLICENSED",
20
+ "dependencies": {
21
+ "@modelcontextprotocol/sdk": "^1.30.0",
22
+ "node-fetch": "^3.3.2",
23
+ "playwright": "^1.63.0",
24
+ "zod": "^4.6.4"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^22.20.2",
28
+ "tsx": "^4.23.13",
29
+ "typescript": "^7.0.2"
30
+ }
31
+ }
package/smithery.yaml ADDED
@@ -0,0 +1,18 @@
1
+ startCommand:
2
+ type: stdio
3
+ configSchema:
4
+ type: object
5
+ required:
6
+ - GUMROAD_LICENSE_KEY
7
+ properties:
8
+ GUMROAD_LICENSE_KEY:
9
+ type: string
10
+ description: Gumroad license key for ForgeQA-MCP
11
+ commandFunction: |-
12
+ (config) => ({
13
+ command: 'node',
14
+ args: ['dist/index.js'],
15
+ env: { GUMROAD_LICENSE_KEY: config.GUMROAD_LICENSE_KEY }
16
+ })
17
+ exampleConfig:
18
+ GUMROAD_LICENSE_KEY: YOUR_GUMROAD_LICENSE_KEY