@usercase/mcp 0.2.8-next.4 → 0.2.8-next.6

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.
@@ -54,10 +54,12 @@ One research is **one flow a participant can walk in one sitting**: 1–4 tasks,
54
54
  - Neutral wording that does not suggest the answer. One thing per question.
55
55
  - Choice answers do not overlap and cover the likely cases; add a free-text question when you cannot guess the answers. `likert_scale` always gets labels for both ends.
56
56
 
57
- **Invite**: the page glob where visitors should be invited (where the flow begins, or the moment the evidence points to), and one short line asking them to take part.
57
+ **Invite**: the page where visitors are invited, and one short line asking them to take part. Participants start the first task on the page where they accepted the invite — nothing takes them elsewhere. So the invite page is where the first task begins, and the first task is something to do from there, never a trip to where they already are. Write it as one kind of page: an exact path, or a path with `:name` for the one part that changes. A `*` covers pages the first route cannot start from.
58
+
59
+ **Time**: estimate the minutes the whole session takes — every task plus every question — and pass it as `minutes`. The invite card and the start screen show it next to your words.
58
60
 
59
61
  **Wording people read** (the start screen, the invite line):
60
- - Say plainly what they will do and roughly how long it takes.
62
+ - Say plainly what they will do. Never state a duration or mention recording: the card and the start screen already show both from the research settings, and a second number can disagree.
61
63
  - The participant is the subject. Never ask for help, never make the team or the site the subject, never thank in advance.
62
64
  - Do not mention the problem you suspect or what others said.
63
65
 
@@ -66,14 +68,14 @@ One research is **one flow a participant can walk in one sitting**: 1–4 tasks,
66
68
  ## 6. Plan each task's route
67
69
 
68
70
  The results tell who went straight from who took a detour only when a task has an expected path: the route the site was built for. Work it out now, from the code, for every task with a page or click to reach (tasks done by staying on a page have none):
69
- - **where it starts**: the page a participant is on when the task begins;
71
+ - **where it starts**: the first task starts on the invite page; every later task starts where the previous one ended;
70
72
  - **the clicks a participant must make**, in order — the main route the UI offers, not a shortcut only you know;
71
- - **where it ends**: the page or the click that counts as done. It must agree with the task's success condition.
73
+ - **where it ends**: the page or the click that counts as done. It must agree with the task's success condition. When participants could reach any item of a kind (a project, a post, a product), the task's page is the route's pattern with `:name` for that part — never the item you happened to open — and while recording, `path_pattern` turns that page into the same pattern.
72
74
  - what the route needs: a signed-in demo account, or a write the safety guard does not send (the last click is then recorded without pressing it).
73
75
 
74
76
  ## 7. Show the user everything at once
75
77
 
76
- Show the whole research compactly: the purpose and its evidence, each task with how it counts as done (in plain words, no XPath) and its route in one line, the questions, and the invite. Say that on their approval you will walk each route in a browser and save the draft with the routes. Change whatever they ask for.
78
+ Show the whole research compactly: the purpose and its evidence, each task with how it counts as done (in plain words, no XPath) and its route in one line, the questions, the invite, and the minutes. Say that on their approval you will walk each route in a browser and save the draft with the routes. Change whatever they ask for.
77
79
 
78
80
  ## 8. On approval: record the routes, then save
79
81
 
package/mcp/server.js CHANGED
@@ -23,7 +23,7 @@ const ws = require("./workspace.js");
23
23
  const persona = require("../src/persona/persona.js");
24
24
  const report = require("./report.js");
25
25
  const peopleRules = require("../src/persona/people.js");
26
- const { PathRecorder } = require("./path.js");
26
+ const { PathRecorder, patternOf } = require("./path.js");
27
27
 
28
28
  const hub = new Hub();
29
29
  const server = new McpServer({ name: "usercase", version: require("../package.json").version });
@@ -706,12 +706,44 @@ server.registerTool(
706
706
  )
707
707
  .optional(),
708
708
  questions: z.array(RQuestion).optional().describe("Asked after all tasks"),
709
- invite: z.object({ page: z.string().describe("Page glob where visitors are invited"), copy: z.string().describe("The invite line, short") }).optional(),
709
+ invite: z.object({ page: z.string().describe("Page glob where visitors are invited"), copy: z.string().describe("The invite line, short. No duration — the card shows minutes") }).optional(),
710
+ minutes: z.number().int().min(1).max(60).optional().describe("Minutes the whole session takes (tasks and questions). Shown on the invite card and the start screen"),
710
711
  basis: z.enum(["real", "virtual", "none"]).describe("What the research came from: real visitor data, virtual user data, or neither (the user's question or the code)"),
711
712
  findingIds: z.array(z.string()).optional().describe("Ids of the findings it answers, from research_context"),
712
713
  },
713
714
  },
714
715
  wrap(async (args) => {
716
+ // 참여자는 초대를 수락한 페이지에서 첫 과제를 시작한다(SDK 는 다른 데로 데려가지 않는다).
717
+ // 첫 과제 경로가 다른 페이지에서 시작하면 첫 단계를 아무도 못 지난다 — 저장 전에 막는다. :이름 은 이름과 상관없이 같은 칸
718
+ // 녹화는 걸은 주소 그대로(/project/zksync)다. 과제에 적은 패턴(/project/:slug)에 맞는 주소는 그 패턴으로 —
719
+ // 안 그러면 그 항목을 고른 사람만 성공·바로 감으로 잡힌다(10/29 bluetags). 에이전트가 path_pattern 을 빠뜨려도 여기서 맞춘다
720
+ const toPattern = (body, pat) => {
721
+ if (!pat || !pat.includes(":")) return false;
722
+ const re = patternOf(pat);
723
+ const fit = (u) => u && u !== pat && !u.includes(":") && re.test(u);
724
+ let hit = false;
725
+ for (const s of body.steps) if (fit(s.url)) (s.url = pat), (hit = true);
726
+ const sc = body.success;
727
+ if (sc && fit(sc.targetUrl)) (sc.targetUrl = pat), (hit = true);
728
+ if (sc && fit(sc.xpathUrl)) (sc.xpathUrl = pat), (hit = true);
729
+ return hit;
730
+ };
731
+ const patternNotes = [];
732
+ for (const [n, p] of recorder.planned) {
733
+ const t = (args.tasks || [])[n - 1];
734
+ const pat = t && (t.success_condition === "element_click" ? t.xpath_url : t.target_url);
735
+ if (t && toPattern(p.body, pat)) patternNotes.push(`- task ${n}: recorded pages matching ${pat} now use that pattern`);
736
+ }
737
+ const first = recorder.planned.get(1);
738
+ if (args.invite && first && toPattern(first.body, args.invite.page)) patternNotes.push(`- task 1: its start page now uses the invite's pattern ${args.invite.page}`);
739
+ if (args.invite && first) {
740
+ const kind = (p) => String(p || "").trim().replace(/:[A-Za-z_]\w*/g, ":x").replace(/\/+$/, "") || "/";
741
+ const start = first.body.steps[0].url;
742
+ if (kind(args.invite.page) !== kind(start))
743
+ throw new Error(
744
+ `Nothing was saved. The invite is on ${args.invite.page}, but the first task's recorded route starts on ${start}. Participants start the first task where they accepted the invite, so these must be the same page: put the invite on ${start}, or record the first route again from the invite page (path_start plan-1). Tell the user which you change.`,
745
+ );
746
+ }
715
747
  const r = await report.saveResearchDraft(args);
716
748
  const tasks = r.tasks || [];
717
749
  if (tasks.length) recorder.addDraft(r.draftId, { title: args.title, website: r.website || "", tasks });
@@ -737,6 +769,7 @@ server.registerTool(
737
769
  `Saved as a draft${r.findings ? `, linked to ${r.findings} finding${r.findings > 1 ? "s" : ""}` : ""}.`,
738
770
  `Open it in the Inbox to review and create it: ${report.dashboardUrl(r.inboxPath)}`,
739
771
  ...(lines.length ? ["", "Expected paths saved with it:", ...lines] : []),
772
+ ...(patternNotes.length ? ["", "Item pages were recorded with the item you opened; they now match any item of that kind:", ...patternNotes] : []),
740
773
  ...(missing.length
741
774
  ? ["", "Tasks without an expected path:", ...missing.map((t) => `- ${t.title} (record later with path_start draft:${r.draftId}:${t.index})`)]
742
775
  : []),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usercase/mcp",
3
- "version": "0.2.8-next.4",
3
+ "version": "0.2.8-next.6",
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",