@askqa/mcp 1.2.2 → 1.2.5
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/marketplace.json +9 -0
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +15 -0
- package/package.json +1 -1
- package/server.js +105 -0
package/README.md
CHANGED
|
@@ -54,6 +54,9 @@ Get your API key from [askqa.ai](https://askqa.ai) after signing in.
|
|
|
54
54
|
| `list_notification_channels` | List notification channels |
|
|
55
55
|
| `remove_notification_channel` | Remove a notification channel |
|
|
56
56
|
| `test_notification_channel` | Send a test notification |
|
|
57
|
+
| `share_test_run` | Make a test run publicly shareable, returns a share URL |
|
|
58
|
+
| `unshare_test_run` | Revoke public sharing for a test run |
|
|
59
|
+
| `get_shared_run` | Fetch a shared run by URL or token (no API key needed); includes test code to recreate it |
|
|
57
60
|
|
|
58
61
|
## Example Conversations
|
|
59
62
|
|
|
@@ -75,6 +78,18 @@ The AI will use `screenshot_url` to inspect the page, write custom Playwright co
|
|
|
75
78
|
|
|
76
79
|
The AI will call `get_test_results` and `get_test_screenshots` to analyze the failure.
|
|
77
80
|
|
|
81
|
+
**Share a test run:**
|
|
82
|
+
|
|
83
|
+
> "Share my latest checkout run with the client"
|
|
84
|
+
|
|
85
|
+
The AI will call `get_test_results` to find the run, then `share_test_run` to generate a public link. Use `unshare_test_run` to revoke access later.
|
|
86
|
+
|
|
87
|
+
**Copy a shared test into your account:**
|
|
88
|
+
|
|
89
|
+
> "I got this link — can you set up the same test for my site? https://askqa.ai/r/..."
|
|
90
|
+
|
|
91
|
+
The AI will call `get_shared_run` to read the test code from the shared link (no API key needed), then `create_test` to add it to your account.
|
|
92
|
+
|
|
78
93
|
## Privacy Policy
|
|
79
94
|
|
|
80
95
|
This extension only makes HTTPS requests to the AskQA API. It does not access files on your computer or collect analytics.
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -1077,6 +1077,111 @@ server.registerTool(
|
|
|
1077
1077
|
}
|
|
1078
1078
|
);
|
|
1079
1079
|
|
|
1080
|
+
server.registerTool(
|
|
1081
|
+
"get_shared_run",
|
|
1082
|
+
{
|
|
1083
|
+
description:
|
|
1084
|
+
"Fetch a publicly shared test run by its share URL or token. No API key required — anyone with the link can call this. " +
|
|
1085
|
+
"Returns run results, step details, and the original test config (test_code, test_template_id, test_params, test_url) " +
|
|
1086
|
+
"so you can recreate the test in your own account using create_test.",
|
|
1087
|
+
readOnlyHint: true,
|
|
1088
|
+
inputSchema: {
|
|
1089
|
+
share_url_or_token: z.string().describe("The share URL (https://askqa.ai/r/TOKEN) or just the token"),
|
|
1090
|
+
},
|
|
1091
|
+
},
|
|
1092
|
+
async ({ share_url_or_token }) => {
|
|
1093
|
+
try {
|
|
1094
|
+
const token = share_url_or_token.includes("/")
|
|
1095
|
+
? share_url_or_token.split("/").pop()
|
|
1096
|
+
: share_url_or_token;
|
|
1097
|
+
|
|
1098
|
+
const res = await fetch(`${API_URL}/api/test-runs/shared/${encodeURIComponent(token)}`);
|
|
1099
|
+
if (res.status === 404) {
|
|
1100
|
+
return { content: [{ type: "text", text: "Shared run not found. The link may be invalid or sharing may have been revoked." }], isError: true };
|
|
1101
|
+
}
|
|
1102
|
+
if (!res.ok) {
|
|
1103
|
+
throw new Error(`API ${res.status}: ${await res.text()}`);
|
|
1104
|
+
}
|
|
1105
|
+
const run = await res.json();
|
|
1106
|
+
|
|
1107
|
+
const lines = [
|
|
1108
|
+
`Run #${run.id} — ${run.test_name || "Unnamed test"} | ${run.status}`,
|
|
1109
|
+
`URL: ${run.test_url || "unknown"}`,
|
|
1110
|
+
`Duration: ${run.result?.durationMs ? (run.result.durationMs / 1000).toFixed(1) + "s" : "N/A"}`,
|
|
1111
|
+
"",
|
|
1112
|
+
];
|
|
1113
|
+
|
|
1114
|
+
if (run.result?.steps?.length) {
|
|
1115
|
+
lines.push("Steps:");
|
|
1116
|
+
for (const s of run.result.steps) {
|
|
1117
|
+
lines.push(` ${s.status === "passed" ? "✓" : "✗"} ${s.name}${s.error ? " — " + s.error : ""}`);
|
|
1118
|
+
}
|
|
1119
|
+
lines.push("");
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
if (run.test_code) {
|
|
1123
|
+
lines.push("Test code (use create_test to recreate in your account):");
|
|
1124
|
+
lines.push("```javascript");
|
|
1125
|
+
lines.push(run.test_code);
|
|
1126
|
+
lines.push("```");
|
|
1127
|
+
} else if (run.test_template_id) {
|
|
1128
|
+
lines.push(`Template: ${run.test_template_id}`);
|
|
1129
|
+
if (run.test_params && Object.keys(run.test_params).length) {
|
|
1130
|
+
lines.push(`Params: ${JSON.stringify(run.test_params)}`);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1135
|
+
} catch (err) {
|
|
1136
|
+
return { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
);
|
|
1140
|
+
|
|
1141
|
+
server.registerTool(
|
|
1142
|
+
"share_test_run",
|
|
1143
|
+
{
|
|
1144
|
+
description: "Make a test run publicly shareable. Returns a share_url that anyone can open without logging in. Calling again on an already-shared run returns the same URL. Use unshare_test_run to revoke access.",
|
|
1145
|
+
inputSchema: {
|
|
1146
|
+
test_run_id: z.coerce.number().describe("The test run ID to share"),
|
|
1147
|
+
},
|
|
1148
|
+
},
|
|
1149
|
+
async ({ test_run_id }) => {
|
|
1150
|
+
try {
|
|
1151
|
+
const data = await apiPost(`/api/test-runs/${test_run_id}/share`, {});
|
|
1152
|
+
return { content: [{ type: "text", text: `Share URL: ${data.share_url}\n\nAnyone with this link can view the test run results without logging in.` }] };
|
|
1153
|
+
} catch (err) {
|
|
1154
|
+
return { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
);
|
|
1158
|
+
|
|
1159
|
+
server.registerTool(
|
|
1160
|
+
"unshare_test_run",
|
|
1161
|
+
{
|
|
1162
|
+
description: "Revoke public sharing for a test run. The share URL will stop working immediately.",
|
|
1163
|
+
destructiveHint: true,
|
|
1164
|
+
inputSchema: {
|
|
1165
|
+
test_run_id: z.coerce.number().describe("The test run ID to stop sharing"),
|
|
1166
|
+
},
|
|
1167
|
+
},
|
|
1168
|
+
async ({ test_run_id }) => {
|
|
1169
|
+
try {
|
|
1170
|
+
const res = await fetch(`${API_URL}/api/test-runs/${test_run_id}/share`, {
|
|
1171
|
+
method: "DELETE",
|
|
1172
|
+
headers: { Authorization: `Bearer ${API_KEY}` },
|
|
1173
|
+
});
|
|
1174
|
+
if (!res.ok && res.status !== 204) {
|
|
1175
|
+
const text = await res.text();
|
|
1176
|
+
throw new Error(`API ${res.status}: ${text}`);
|
|
1177
|
+
}
|
|
1178
|
+
return { content: [{ type: "text", text: `Sharing disabled for run #${test_run_id}. The public link no longer works.` }] };
|
|
1179
|
+
} catch (err) {
|
|
1180
|
+
return { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
);
|
|
1184
|
+
|
|
1080
1185
|
const transport = new StdioServerTransport();
|
|
1081
1186
|
await server.connect(transport);
|
|
1082
1187
|
console.error("AskQA MCP server running on stdio");
|