@askqa/mcp 1.1.1 → 1.1.2
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-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/server.js +97 -5
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -170,8 +170,19 @@ const server = new McpServer(
|
|
|
170
170
|
'When the user asks whether something is working (e.g. "is checkout working?", "is the site up?"),',
|
|
171
171
|
"your FIRST step should be to call list_tests to find a matching test by name or URL,",
|
|
172
172
|
"then call get_test_results for that test to check the latest run status and step details.",
|
|
173
|
-
"If the latest run passed, confirm it
|
|
173
|
+
"If the latest run passed, confirm it's working. If it failed, report what failed.",
|
|
174
174
|
"Only call run_test if the user explicitly asks to run a new test — checking status should use existing results.",
|
|
175
|
+
"",
|
|
176
|
+
"When the user asks to monitor a site, use list_templates to see available templates.",
|
|
177
|
+
"There are two kinds of templates:",
|
|
178
|
+
"",
|
|
179
|
+
"1. Universal templates (e.g. 'quick-checks') — work on any website with no configuration.",
|
|
180
|
+
" Just call create_test with template_id and you're done.",
|
|
181
|
+
"",
|
|
182
|
+
"2. Site-specific templates (e.g. 'shopify-cart') — need to discover CSS selectors for the target site.",
|
|
183
|
+
" Call detect_template first to probe the site and generate standalone Playwright code,",
|
|
184
|
+
" then call create_test with that generated code (not the template_id).",
|
|
185
|
+
" Templates that support detection have supportsDetection: true in list_templates output.",
|
|
175
186
|
].join("\n"),
|
|
176
187
|
}
|
|
177
188
|
);
|
|
@@ -179,13 +190,94 @@ const server = new McpServer(
|
|
|
179
190
|
server.registerTool(
|
|
180
191
|
"list_templates",
|
|
181
192
|
{
|
|
182
|
-
description: "List available test templates.
|
|
193
|
+
description: "List available test templates with usage hints. Some templates work directly with create_test (universal), others need detect_template first to discover site-specific selectors (site-specific).",
|
|
183
194
|
readOnlyHint: true,
|
|
184
195
|
},
|
|
185
196
|
async () => {
|
|
186
197
|
try {
|
|
187
198
|
const data = await apiGet("/api/tests/templates");
|
|
188
|
-
|
|
199
|
+
const lines = [];
|
|
200
|
+
for (const t of data.templates) {
|
|
201
|
+
lines.push(`${t.id}`);
|
|
202
|
+
lines.push(` Name: ${t.name}`);
|
|
203
|
+
lines.push(` Description: ${t.description}`);
|
|
204
|
+
lines.push(` Steps: ${t.steps.join(", ")}`);
|
|
205
|
+
if (t.supportsCodeGeneration) {
|
|
206
|
+
lines.push(` supportsDetection: true`);
|
|
207
|
+
lines.push(` Usage: call detect_template → get generated code → create_test with code`);
|
|
208
|
+
} else {
|
|
209
|
+
lines.push(` Usage: call create_test with template_id="${t.id}" directly`);
|
|
210
|
+
}
|
|
211
|
+
lines.push("");
|
|
212
|
+
}
|
|
213
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
214
|
+
} catch (err) {
|
|
215
|
+
return { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
server.registerTool(
|
|
221
|
+
"detect_template",
|
|
222
|
+
{
|
|
223
|
+
description: "Run template detection against a URL to discover selectors and generate custom test code. This is the fastest way to create a custom test — detection runs the template flow against the site, discovers which CSS selectors work, and generates standalone Playwright test code with those selectors baked in. Use the generated code with create_test to save it.",
|
|
224
|
+
readOnlyHint: true,
|
|
225
|
+
inputSchema: {
|
|
226
|
+
template_id: z.string().describe("Template ID from list_templates (e.g. 'shopify-cart')"),
|
|
227
|
+
url: z.string().describe("The target URL to detect against (e.g. 'https://my-store.myshopify.com')"),
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
async ({ template_id, url }) => {
|
|
231
|
+
try {
|
|
232
|
+
const result = await apiPost(`/api/tests/templates/${template_id}/detect`, { url });
|
|
233
|
+
const content = [];
|
|
234
|
+
const lines = [];
|
|
235
|
+
|
|
236
|
+
const statusLabel = result.status === "passed" ? "PASSED" : "FAILED";
|
|
237
|
+
lines.push(`Detection: ${statusLabel}`);
|
|
238
|
+
lines.push("");
|
|
239
|
+
|
|
240
|
+
if (result.steps) {
|
|
241
|
+
lines.push("Steps:");
|
|
242
|
+
for (const step of result.steps) {
|
|
243
|
+
const icon = step.status === "passed" ? "+" : "x";
|
|
244
|
+
lines.push(` ${icon} ${step.name} — ${step.status}`);
|
|
245
|
+
if (step.error) lines.push(` Error: ${step.error}`);
|
|
246
|
+
}
|
|
247
|
+
lines.push("");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (result.selectors) {
|
|
251
|
+
lines.push("Discovered selectors:");
|
|
252
|
+
for (const [key, value] of Object.entries(result.selectors)) {
|
|
253
|
+
lines.push(` ${key}: ${value}`);
|
|
254
|
+
}
|
|
255
|
+
lines.push("");
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (result.code) {
|
|
259
|
+
lines.push("Generated test code (use with create_test):");
|
|
260
|
+
lines.push("```");
|
|
261
|
+
lines.push(result.code);
|
|
262
|
+
lines.push("```");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
content.push({ type: "text", text: lines.join("\n") });
|
|
266
|
+
|
|
267
|
+
// Fetch screenshots for each step
|
|
268
|
+
if (result.steps) {
|
|
269
|
+
for (const step of result.steps) {
|
|
270
|
+
if (!step.screenshot) continue;
|
|
271
|
+
const screenshotUrl = `${API_URL}/api/screenshots/${result.executionId}/${step.screenshot}`;
|
|
272
|
+
const base64 = await fetchScreenshot(screenshotUrl);
|
|
273
|
+
if (base64) {
|
|
274
|
+
content.push({ type: "text", text: `Screenshot: ${step.name}` });
|
|
275
|
+
content.push({ type: "image", data: base64, mimeType: "image/png" });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return { content };
|
|
189
281
|
} catch (err) {
|
|
190
282
|
return { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
|
|
191
283
|
}
|
|
@@ -195,7 +287,7 @@ server.registerTool(
|
|
|
195
287
|
server.registerTool(
|
|
196
288
|
"create_test",
|
|
197
289
|
{
|
|
198
|
-
description: "Create a saved test. Use template_id for
|
|
290
|
+
description: "Create a saved test. Use template_id for universal templates (e.g. 'quick-checks') that work on any site. Use code for custom tests or for site-specific templates after running detect_template. Provide template_id or code, not both.",
|
|
199
291
|
destructiveHint: true,
|
|
200
292
|
inputSchema: {
|
|
201
293
|
name: z.string().describe("A name for this test (e.g. 'Homepage health check')"),
|
|
@@ -403,7 +495,7 @@ server.registerTool(
|
|
|
403
495
|
code: z.string().optional().describe("Updated custom Playwright test code"),
|
|
404
496
|
template_id: z.string().optional().describe("Updated template ID"),
|
|
405
497
|
params: z.record(z.string()).optional().describe("Updated template parameters"),
|
|
406
|
-
secrets: z.record(z.string()).nullable().optional().describe("Updated secrets (pass null to clear)"),
|
|
498
|
+
secrets: z.record(z.string()).nullable().optional().describe("Updated secrets (pass null to clear). Encrypted at rest, never returned in API responses — must be provided again when updating a test that uses secrets."),
|
|
407
499
|
headers: z.record(z.string()).nullable().optional().describe("Updated HTTP headers (pass null to clear)"),
|
|
408
500
|
enable_test_mode: z.boolean().optional().describe("Send X-AskQA-Secret header to the target site, enabling test mode on sites that support it (default: true)"),
|
|
409
501
|
},
|