@askqa/mcp 1.2.0 → 1.2.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "askqa",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "AskQA skills — set up notifications and monitoring for your websites",
5
5
  "mcpServers": {
6
6
  "askqa": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askqa/mcp",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "MCP server for AskQA — monitor websites with automated tests by chatting with AI",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/server.js CHANGED
@@ -138,6 +138,9 @@ function buildTestRunText(testRun, testName) {
138
138
  }
139
139
 
140
140
  lines.push(` View details: ${testRun.details_url || `${WEBSITE_URL}/runs/${testRun.id}`}`);
141
+ if (testRun.trace_viewer_url) {
142
+ lines.push(` View trace: ${testRun.trace_viewer_url}`);
143
+ }
141
144
 
142
145
  return lines.join("\n");
143
146
  }
@@ -381,7 +384,7 @@ server.registerTool(
381
384
  server.registerTool(
382
385
  "validate_test",
383
386
  {
384
- description: "Dry-run custom Playwright test code against a URL. Returns execution results, screenshots, and page structure for debugging. Steps continue even on failure to maximize debug signal. Use this to iterate on code before calling create_test.",
387
+ description: "Start here when writing a new test. Dry-runs Playwright code against a URL without saving it — returns step results, screenshots, and page structure. Steps continue even on failure for maximum debug signal. Iterate here until the test passes, then call create_test to save it.",
385
388
  readOnlyHint: true,
386
389
  inputSchema: {
387
390
  code: z.string().describe("Custom Playwright test code. Must define an async function test({ page, step, log })."),
@@ -390,28 +393,33 @@ server.registerTool(
390
393
  },
391
394
  async ({ code, url }) => {
392
395
  try {
393
- const result = await apiPost("/api/tests/validate", { code, url });
396
+ // POST returns immediately with test_run_id; poll until done (same as run_test)
397
+ const { test_run_id } = await apiPost("/api/tests/validate", { code, url });
398
+ const testRun = await pollTestRun(test_run_id);
399
+ const runResult = testRun.result || {};
394
400
  const content = [];
395
401
 
396
402
  // Build text summary
397
- const icon = result.status === "passed" ? "" : "";
398
- const lines = [`${icon} Validation: ${result.status}`];
399
- if (result.durationMs) lines.push(` Duration: ${(result.durationMs / 1000).toFixed(1)}s`);
400
- if (result.error) lines.push(` Error: ${result.error}`);
401
- for (const step of (result.steps || [])) {
403
+ const overallStatus = runResult.status || (testRun.status === "completed" ? "passed" : "failed");
404
+ const icon = overallStatus === "passed" ? "✓" : "✗";
405
+ const lines = [`${icon} Validation: ${overallStatus}`];
406
+ if (runResult.durationMs) lines.push(` Duration: ${(runResult.durationMs / 1000).toFixed(1)}s`);
407
+ if (testRun.error) lines.push(` Error: ${testRun.error}`);
408
+ if (runResult.error) lines.push(` Error: ${runResult.error}`);
409
+ for (const step of (runResult.steps || [])) {
402
410
  const stepIcon = step.status === "passed" ? "✓" : step.status === "failed" ? "✗" : "?";
403
411
  lines.push(` ${stepIcon} ${step.name} — ${step.status}`);
404
412
  if (step.error) lines.push(` Error: ${step.error}`);
405
413
  }
406
- if (result.logs?.length) {
414
+ if (runResult.logs?.length) {
407
415
  lines.push(" Logs:");
408
- for (const msg of result.logs) lines.push(` ${msg}`);
416
+ for (const msg of runResult.logs) lines.push(` ${msg}`);
409
417
  }
410
418
  content.push({ type: "text", text: lines.join("\n") });
411
419
 
412
420
  // Include page info for debugging selectors
413
- if (result.pageInfo) {
414
- const info = result.pageInfo;
421
+ if (runResult.pageInfo) {
422
+ const info = runResult.pageInfo;
415
423
  const infoLines = ["", "Page structure (for fixing selectors):"];
416
424
  if (info.buttons?.length) {
417
425
  infoLines.push(" Buttons:");
@@ -431,14 +439,10 @@ server.registerTool(
431
439
  content.push({ type: "text", text: infoLines.join("\n") });
432
440
  }
433
441
 
434
- // Include screenshots as labeled images
435
- if (result.screenshots) {
436
- for (const [stepName, base64] of Object.entries(result.screenshots)) {
437
- if (base64) {
438
- content.push({ type: "text", text: `Screenshot: ${stepName}` });
439
- content.push({ type: "image", data: base64, mimeType: "image/png" });
440
- }
441
- }
442
+ // Include screenshots from storage (via execution_id)
443
+ if (testRun.execution_id) {
444
+ const screenshots = await buildTestRunScreenshots(testRun);
445
+ content.push(...screenshots);
442
446
  }
443
447
 
444
448
  return { content };