@usercase/mcp 0.2.4-next.3 → 0.2.4-next.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/agents/path.md +49 -49
- package/mcp/path.js +18 -10
- package/mcp/server.js +1 -1
- package/package.json +1 -1
package/agents/path.md
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
# Record the expected path for a Usercase research
|
|
2
|
-
|
|
3
|
-
Research: {{research}}
|
|
4
|
-
|
|
5
|
-
A Usercase research asks real participants to do tasks on this site. For each task, the dashboard splits results into **went straight**, **took a detour** and **failed** — but only if the task has an **expected path**: the route the team intends people to take. Without one, every success counts as "went straight", so the results cannot show where people wander.
|
|
6
|
-
|
|
7
|
-
You record that path by walking it yourself in a browser. Unlike the virtual visitors, **you may read the code** — the expected path is the route the site was built for, and the code is where that intent lives.
|
|
8
|
-
|
|
9
|
-
## 1. See the tasks
|
|
10
|
-
|
|
11
|
-
Call `path_tasks` with the research id. For each task you get what the participant is told, how it counts as done, and whether a path exists. Record paths for the tasks that have none. If every task already has one, ask the user which to redo.
|
|
12
|
-
|
|
13
|
-
## 2. Work out the intended route — before opening the browser
|
|
14
|
-
|
|
15
|
-
For each task, from the task text and the code (routes, pages, the components on them):
|
|
16
|
-
- **where it starts**: the page a participant is on when the task begins. If the task text names a situation ("you are on a jacket's product page"), start there; otherwise the front page.
|
|
17
|
-
- **the clicks a participant must make**, in order — the main route the UI offers (the menu, the primary button), not a shortcut only you know (a typed URL, a hidden link).
|
|
18
|
-
- **where it ends**: the page or the click that means the task is done. Compare it with the task's success condition from `path_tasks`. If they disagree, or the task has none that fits, note it — the user decides in step 5.
|
|
19
|
-
|
|
20
|
-
Tell the user the route in one line per task (e.g. "Front page → Pricing → 'Start free trial' → sign-up form") and go on. Stop and ask only when the code leaves the route genuinely unclear.
|
|
21
|
-
|
|
22
|
-
## 3. Walk it
|
|
23
|
-
|
|
24
|
-
- `path_start` with the task and the URL where the route starts. A local dev server is fine (only paths are saved, not the host). Use `account` if the route is for signed-in users (demo accounts from `login`).
|
|
25
|
-
- `path_click` each click of the route. Choose the element a participant would use — the visible button or menu item.
|
|
26
|
-
- Things that are not part of the route (closing a cookie banner or a pop-up): `path_click` with `record: false`. Search terms or quantities: `path_type`.
|
|
27
|
-
- **Picking one of many** (a product, an announcement, a project from a list): participants will pick a different one, and a recorded click points at the exact item you picked — everyone else would count as taking a detour. So click it with `record: false`, and turn the page it opens into a pattern with `path_pattern` (`/notice/6553c3ba` → `/notice/:id`, parameter names from the route definitions in the code). The page step is what matches them.
|
|
28
|
-
- Record a click as a step only when every participant must press that same control (a menu item, the main button).
|
|
29
|
-
- **The safety guard does not send writes** (sign-ups, posts, orders) unless the user chose so with `set_writes`, and never presses pay or delete buttons. If the route's last step is such a button, record it with `press: false` — it becomes the final click. If the route needs a write *in the middle* (an item must be in the cart first), stop and tell the user: the path can only go as far as the guard allows, unless they let writes through on a test site.
|
|
30
|
-
|
|
31
|
-
Do not change any code.
|
|
32
|
-
|
|
33
|
-
## 4. Check it
|
|
34
|
-
|
|
35
|
-
Call `path_check`. It walks the path again in a fresh browser. If it fails:
|
|
36
|
-
- "not found" or "now holds …": the element sits at a position that changes between visits (list order, a rotating banner). Record again with a more stable element — a menu link, a heading link — or use a pattern for the page.
|
|
37
|
-
- "landed on … instead": the page address changes; use `path_pattern`.
|
|
38
|
-
|
|
39
|
-
Record again with `path_start` when needed. Only a passing check can be saved.
|
|
40
|
-
|
|
41
|
-
## 5. Show the user, then save
|
|
42
|
-
|
|
43
|
-
Show the user the steps and the screenshots `path_check` lists (open them so they can see each step). Ask:
|
|
44
|
-
- whether this is the route they intend participants to take, and
|
|
45
|
-
- if the task's success condition did not match the route's end (step 2), whether to change it: **page** = done on reaching the last page of the path, **click** = done on the last recorded click, **keep** = leave it.
|
|
46
|
-
|
|
47
|
-
On their approval, call `path_save` (with `success` if they chose to change it). Then go on to the next task.
|
|
48
|
-
|
|
49
|
-
When all are done, tell the user which tasks now have a path, and any you could not record and why.
|
|
1
|
+
# Record the expected path for a Usercase research
|
|
2
|
+
|
|
3
|
+
Research: {{research}}
|
|
4
|
+
|
|
5
|
+
A Usercase research asks real participants to do tasks on this site. For each task, the dashboard splits results into **went straight**, **took a detour** and **failed** — but only if the task has an **expected path**: the route the team intends people to take. Without one, every success counts as "went straight", so the results cannot show where people wander.
|
|
6
|
+
|
|
7
|
+
You record that path by walking it yourself in a browser. Unlike the virtual visitors, **you may read the code** — the expected path is the route the site was built for, and the code is where that intent lives.
|
|
8
|
+
|
|
9
|
+
## 1. See the tasks
|
|
10
|
+
|
|
11
|
+
Call `path_tasks` with the research id. For each task you get what the participant is told, how it counts as done, and whether a path exists. Record paths for the tasks that have none. If every task already has one, ask the user which to redo.
|
|
12
|
+
|
|
13
|
+
## 2. Work out the intended route — before opening the browser
|
|
14
|
+
|
|
15
|
+
For each task, from the task text and the code (routes, pages, the components on them):
|
|
16
|
+
- **where it starts**: the page a participant is on when the task begins. If the task text names a situation ("you are on a jacket's product page"), start there; otherwise the front page.
|
|
17
|
+
- **the clicks a participant must make**, in order — the main route the UI offers (the menu, the primary button), not a shortcut only you know (a typed URL, a hidden link).
|
|
18
|
+
- **where it ends**: the page or the click that means the task is done. Compare it with the task's success condition from `path_tasks`. If they disagree, or the task has none that fits, note it — the user decides in step 5.
|
|
19
|
+
|
|
20
|
+
Tell the user the route in one line per task (e.g. "Front page → Pricing → 'Start free trial' → sign-up form") and go on. Stop and ask only when the code leaves the route genuinely unclear.
|
|
21
|
+
|
|
22
|
+
## 3. Walk it
|
|
23
|
+
|
|
24
|
+
- `path_start` with the task and the URL where the route starts. A local dev server is fine (only paths are saved, not the host). Use `account` if the route is for signed-in users (demo accounts from `login`).
|
|
25
|
+
- `path_click` each click of the route. Choose the element a participant would use — the visible button or menu item.
|
|
26
|
+
- Things that are not part of the route (closing a cookie banner or a pop-up): `path_click` with `record: false`. Search terms or quantities: `path_type`.
|
|
27
|
+
- **Picking one of many** (a product, an announcement, a project from a list): participants will pick a different one, and a recorded click points at the exact item you picked — everyone else would count as taking a detour. So click it with `record: false`, and turn the page it opens into a pattern with `path_pattern` (`/notice/6553c3ba` → `/notice/:id`, parameter names from the route definitions in the code). The page step is what matches them.
|
|
28
|
+
- Record a click as a step only when every participant must press that same control (a menu item, the main button).
|
|
29
|
+
- **The safety guard does not send writes** (sign-ups, posts, orders) unless the user chose so with `set_writes`, and never presses pay or delete buttons. If the route's last step is such a button, record it with `press: false` — it becomes the final click. If the route needs a write *in the middle* (an item must be in the cart first), stop and tell the user: the path can only go as far as the guard allows, unless they let writes through on a test site.
|
|
30
|
+
|
|
31
|
+
Do not change any code.
|
|
32
|
+
|
|
33
|
+
## 4. Check it
|
|
34
|
+
|
|
35
|
+
Call `path_check`. It walks the path again in a fresh browser. If it fails:
|
|
36
|
+
- "not found" or "now holds …": the element sits at a position that changes between visits (list order, a rotating banner). Record again with a more stable element — a menu link, a heading link — or use a pattern for the page.
|
|
37
|
+
- "landed on … instead": the page address changes; use `path_pattern`.
|
|
38
|
+
|
|
39
|
+
Record again with `path_start` when needed. Only a passing check can be saved.
|
|
40
|
+
|
|
41
|
+
## 5. Show the user, then save
|
|
42
|
+
|
|
43
|
+
Show the user the steps and the screenshots `path_check` lists (open them so they can see each step). Ask:
|
|
44
|
+
- whether this is the route they intend participants to take, and
|
|
45
|
+
- if the task's success condition did not match the route's end (step 2), whether to change it: **page** = done on reaching the last page of the path, **click** = done on the last recorded click, **keep** = leave it. With page or click the path is saved only up to that point — say so when the route goes on after it (e.g. a page that opens after the last click).
|
|
46
|
+
|
|
47
|
+
On their approval, call `path_save` (with `success` if they chose to change it). Then go on to the next task.
|
|
48
|
+
|
|
49
|
+
When all are done, tell the user which tasks now have a path, and any you could not record and why.
|
package/mcp/path.js
CHANGED
|
@@ -366,19 +366,27 @@ class PathRecorder {
|
|
|
366
366
|
async save({ success = "keep" }) {
|
|
367
367
|
const c = this.need();
|
|
368
368
|
if (!c.checked || c.checked !== this.signature()) throw new Error("Run path_check first — only a path that passed, unchanged since, can be saved.");
|
|
369
|
+
// 경로는 성공 지점에서 끝난다 — 성공하는 순간 과제가 끝나서 그 뒤 단계는 아무도 못 채운다(모두 "돌아감"이 된다)
|
|
370
|
+
const type = success === "page" ? "URL" : success === "click" ? "XPATH" : null;
|
|
371
|
+
const end = type ? c.steps.map((s) => s.type).lastIndexOf(type) : c.steps.length - 1;
|
|
372
|
+
if (end < 0) throw new Error("There is no recorded click to use as the success condition.");
|
|
373
|
+
const last = c.steps[end];
|
|
374
|
+
const steps = c.steps.slice(0, end + 1);
|
|
375
|
+
const cut = c.steps.slice(end + 1);
|
|
369
376
|
const body = {
|
|
370
|
-
steps:
|
|
377
|
+
steps: steps.map((s) => ({ type: s.type, url: s.url, xpath: s.xpath || "", classes: s.classes || [], ids: s.ids || [], image: fs.readFileSync(s.image).toString("base64") })),
|
|
371
378
|
};
|
|
372
|
-
if (success === "page") {
|
|
373
|
-
|
|
374
|
-
body.success = { condition: "url_entry", targetUrl: last.url };
|
|
375
|
-
} else if (success === "click") {
|
|
376
|
-
const last = [...c.steps].reverse().find((s) => s.type === "XPATH");
|
|
377
|
-
if (!last) throw new Error("There is no recorded click to use as the success condition.");
|
|
378
|
-
body.success = { condition: "element_click", xpath: last.xpath, xpathUrl: last.url, classes: last.classes, ids: last.ids };
|
|
379
|
-
}
|
|
379
|
+
if (success === "page") body.success = { condition: "url_entry", targetUrl: last.url };
|
|
380
|
+
else if (success === "click") body.success = { condition: "element_click", xpath: last.xpath, xpathUrl: last.url, classes: last.classes, ids: last.ids };
|
|
380
381
|
const r = await report.savePath(c.taskId, body);
|
|
381
|
-
const done =
|
|
382
|
+
const done = [
|
|
383
|
+
`Saved the expected path for task ${c.taskId} ("${c.title}"): ${r.steps} steps${r.success ? `, success condition set to ${r.success === "url_entry" ? `reaching ${last.url}` : `clicking "${last.label || last.tag}"`}` : ""}. The dashboard's task results now separate going straight from detours.`,
|
|
384
|
+
cut.length
|
|
385
|
+
? `Left out the ${cut.length} step(s) after the success point (${cut.map((s) => (s.type === "URL" ? `page ${s.url}` : `click "${s.label || s.tag}"`)).join(", ")}) — the task ends at the success point, so no participant could ever reach them. Tell the user.`
|
|
386
|
+
: "",
|
|
387
|
+
]
|
|
388
|
+
.filter(Boolean)
|
|
389
|
+
.join("\n");
|
|
382
390
|
await this.stop();
|
|
383
391
|
return done;
|
|
384
392
|
}
|
package/mcp/server.js
CHANGED
|
@@ -553,7 +553,7 @@ server.registerTool(
|
|
|
553
553
|
"path_save",
|
|
554
554
|
{
|
|
555
555
|
description:
|
|
556
|
-
"Upload the checked path to the dashboard, replacing the task's expected path. Only after the user approved it. success: keep (default) leaves the task's success condition; page = done on reaching the last page of the path; click = done on the last recorded click. Ask the user before changing it.",
|
|
556
|
+
"Upload the checked path to the dashboard, replacing the task's expected path. Only after the user approved it. success: keep (default) leaves the task's success condition; page = done on reaching the last page of the path; click = done on the last recorded click. With page or click the saved path ends at that point (later steps are left out: the task ends there, so nobody could reach them). Ask the user before changing it.",
|
|
557
557
|
inputSchema: { success: z.enum(["keep", "page", "click"]).optional() },
|
|
558
558
|
},
|
|
559
559
|
wrap(async ({ success }) => text(await recorder.save({ success }))),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usercase/mcp",
|
|
3
|
-
"version": "0.2.4-next.
|
|
3
|
+
"version": "0.2.4-next.5",
|
|
4
4
|
"description": "Virtual users that browse your website and tell you what they thought — an MCP server for Claude Code",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://usercase.io",
|