@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 +4 -0
- package/dist/auth.js +23 -0
- package/dist/auth.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +340 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -0
- package/smithery.yaml +18 -0
package/dist/auth.d.ts
ADDED
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
|
package/dist/auth.js.map
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
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
|