@usercase/mcp 0.0.0-stage → 0.2.1-next.0
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/README.md +142 -2
- package/agents/install.md +26 -0
- package/agents/path.md +49 -0
- package/agents/question.md +62 -0
- package/agents/research.md +113 -0
- package/agents/usercase-visitor.md +112 -0
- package/mcp/hub.js +760 -0
- package/mcp/path.js +394 -0
- package/mcp/report.js +207 -0
- package/mcp/server.js +679 -0
- package/mcp/workspace.js +182 -0
- package/package.json +41 -4
- package/src/browser/blocked.js +180 -0
- package/src/browser/card.js +269 -0
- package/src/browser/guard.js +211 -0
- package/src/browser/hand.js +368 -0
- package/src/browser/open.js +59 -0
- package/src/browser/page.js +353 -0
- package/src/browser/policy.js +154 -0
- package/src/browser/real-chrome.js +242 -0
- package/src/browser/robots.js +73 -0
- package/src/browser/signin.js +66 -0
- package/src/persona/people.js +104 -0
- package/src/persona/persona.js +469 -0
- package/src/prompts/answer.md +50 -0
- package/src/prompts/people.md +38 -0
- package/src/prompts/step.md +36 -0
- package/src/run/answer.js +94 -0
- package/src/run/diag.js +64 -0
- package/src/run/entry.js +76 -0
- package/src/run/runner.js +205 -0
- package/src/run/visit.js +693 -0
package/README.md
CHANGED
|
@@ -1,3 +1,143 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Usercase MCP
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Research your website with virtual users, before you have real ones.
|
|
4
|
+
|
|
5
|
+
Open your repository in Claude Code and ask it to research your site. Claude reads your code to understand what the site is and who it is for, then casts a handful of virtual users. Each one visits the site in a real browser on your machine, looks around with their own goal, and answers the Usercase survey cards they happen to meet. Their visits and answers show up in your Usercase dashboard.
|
|
6
|
+
|
|
7
|
+
- **Free.** The AI runs on your own Claude subscription.
|
|
8
|
+
- **Runs on your machine.** Your browser, your network, your staging or demo accounts. Passwords never pass through Usercase.
|
|
9
|
+
- **Visitors only see the screen.** Claude may read your code to plan the research, but the visitors cannot. A visitor who knows the code finds buttons real people can't, and the research loses its value.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- [Claude Code](https://docs.claude.com/en/docs/claude-code)
|
|
14
|
+
- Node.js 18 or later
|
|
15
|
+
- Google Chrome (or run `npx playwright install chromium` once)
|
|
16
|
+
- A Usercase project with the SDK installed on your site, so the survey cards appear
|
|
17
|
+
|
|
18
|
+
## Connect
|
|
19
|
+
|
|
20
|
+
1. In the Usercase dashboard, open **Settings → Integrations → API keys** and create a key. It starts with `eb_sk_` and is shown once.
|
|
21
|
+
2. In your repository folder, run:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
claude mcp add usercase -e USERCASE_API_KEY=eb_sk_... -- npx -y @usercase/mcp@latest
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This is saved to your Claude Code settings (`~/.claude.json`), not to your repository. `@latest` picks up new versions each time Claude Code starts.
|
|
28
|
+
|
|
29
|
+
## Install the script
|
|
30
|
+
|
|
31
|
+
The survey cards come from the Usercase script on your site. If it isn't there yet, let Claude Code add it:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
/mcp__usercase__install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Claude gets your project's script (from the API key), puts it once in your root layout or document head, shows you the diff, and opens your local site to check that it loads. It doesn't commit or deploy. Real visitors are recorded once you deploy the change.
|
|
38
|
+
|
|
39
|
+
## Run a research
|
|
40
|
+
|
|
41
|
+
In Claude Code:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
/mcp__usercase__research https://staging.example.com 5
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The first time, Claude installs the visitor agent into `~/.claude/agents/` (not your repository) and may ask you to restart Claude Code. Then it:
|
|
48
|
+
|
|
49
|
+
1. checks the site once: whether the SDK is running, whether a firewall blocks it, what the first screen shows
|
|
50
|
+
2. reads your repository to understand the site
|
|
51
|
+
3. casts the people and saves them to `.usercase/people.json`, where you can edit them
|
|
52
|
+
4. sends one visitor per person, up to 5 at a time
|
|
53
|
+
5. summarizes what happened
|
|
54
|
+
|
|
55
|
+
### Keep it going
|
|
56
|
+
|
|
57
|
+
The number is how many people visit in total (default 5). Above 5, Claude works in rounds of 5: you approve the first list, and after that it casts new people unlike the earlier ones, round after round, until the total is reached.
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
/mcp__usercase__research https://staging.example.com 30
|
|
61
|
+
/mcp__usercase__research https://staging.example.com unlimited
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
With `unlimited` it keeps going until you press Esc. Every visit uses your Claude usage.
|
|
65
|
+
|
|
66
|
+
When a visit ends, it is sent to your dashboard (**Visits**). If the upload fails, it is kept in `.usercase/outbox.jsonl` and sent with the next one.
|
|
67
|
+
|
|
68
|
+
### Sites behind a sign-in
|
|
69
|
+
|
|
70
|
+
Ask Claude to sign in first. It opens Chrome (or Edge) with a fresh, empty profile and **you** sign in with a demo account, once per account. Google and other sign-in buttons work there. Your usual browser profile is never touched, and the temporary profile is deleted afterwards. Several accounts keep the signed-in visitors from all sharing one history. Only the sessions are saved (`.usercase/sessions/`), never the password. Claude checks that each sign-in worked, and asks you to sign in again only when one has expired.
|
|
71
|
+
|
|
72
|
+
## Record a task's expected path
|
|
73
|
+
|
|
74
|
+
A research's task results separate people who **went straight** from those who **took a detour** — once the task has an expected path. The task screen in the dashboard shows a command for it:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
/mcp__usercase__path <research id>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Claude reads your code to work out the route each task is meant to take, tells you the route, and walks it in a browser. It then walks it again in a fresh browser to make sure every step is found the same way, shows you the screenshots, and saves the path only after you approve it. Picking one item from a list (a product, a post) is not tied to the item Claude happened to open — any item of that kind counts. A local dev server is fine; only page paths are saved.
|
|
81
|
+
|
|
82
|
+
## Make a question for a precise moment
|
|
83
|
+
|
|
84
|
+
The dashboard's **New question** picks one simple moment (on arrival, after a pause, when leaving, after a click). For anything more precise, ask from your repository:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
/mcp__usercase__question people who search but don't open any result for 10 seconds
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Claude reads your code for the real routes and button labels, writes the moment as a series of steps, and runs it over your recent real visits to show how many it matches and what those visitors did. It shows you the question and its answers, and saves it only after you approve — as a draft in your Inbox. Nobody sees it until you turn it on there. Needs the API key.
|
|
91
|
+
|
|
92
|
+
## What the visitors will and won't do
|
|
93
|
+
|
|
94
|
+
- They identify themselves on every request (see below), and their answers are counted separately from real visitors. This cannot be turned off.
|
|
95
|
+
- By default they don't write to your site: only `GET`, `HEAD` and `OPTIONS` requests are sent. They may still press Post, Save or Like like a person would, but nothing reaches your site, and the visitor is told it was not sent. Before the first round Claude asks whether you want those sent for real (for a test or staging site). Your answer is kept in `.usercase/settings.json`. With writes sent, signed-in visitors use the demo account as their own — they may change its profile name or settings, follow, save and post — so use demo accounts only.
|
|
96
|
+
- They stay on your site and block analytics tools such as Google Analytics and Hotjar.
|
|
97
|
+
- Either way they never pay, delete, connect a wallet, sign out or change a password, and never type into personal fields such as email or phone.
|
|
98
|
+
- They stop when blocked. They do not solve CAPTCHAs or get around bot protection.
|
|
99
|
+
- When they cannot do something on your site, a short diagnostic (what failed and the element's shape, never its text) is sent to Usercase so the tool gets better. Turn it off with `USERCASE_DIAGNOSTICS=off`.
|
|
100
|
+
|
|
101
|
+
## Running on a production site
|
|
102
|
+
|
|
103
|
+
Staging is best: bot protection is usually off and analytics are separate. If you run on production, set three things up first, or the visitors may be blocked as an attack or counted as real traffic.
|
|
104
|
+
|
|
105
|
+
Every visitor request carries:
|
|
106
|
+
|
|
107
|
+
| What | Value | Use it to |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| Header `X-Usercase-Agent` | the kind of visitor, e.g. `qa-explore` | separate them in logs and server-side analytics |
|
|
110
|
+
| Header `X-Usercase-Key` | your own secret, sent only to your site | let them through your firewall or CDN |
|
|
111
|
+
| User-Agent suffix | `UsercaseQA/1.0 (<kind>)` | exclude them in browser analytics and in logs that drop headers |
|
|
112
|
+
|
|
113
|
+
1. **Let them through your firewall.** Make a random secret (16+ characters, e.g. `node -e "console.log(require('crypto').randomBytes(24).toString('base64url'))"`), pass it as `USERCASE_AGENT_KEY`, and allow requests whose `X-Usercase-Key` header matches it. Don't allow on `X-Usercase-Agent` alone; anyone can send that.
|
|
114
|
+
2. **Exclude them from analytics.** Filter out sessions whose user agent contains `UsercaseQA`.
|
|
115
|
+
3. **Separate them in logs** by the header or the user-agent suffix.
|
|
116
|
+
|
|
117
|
+
## Settings
|
|
118
|
+
|
|
119
|
+
Pass with `claude mcp add ... -e NAME=value`.
|
|
120
|
+
|
|
121
|
+
| Name | What it does |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `USERCASE_API_KEY` | Your project API key. Without it, visits stay on your machine only. |
|
|
124
|
+
| `USERCASE_AGENT_KEY` | Your firewall secret (see above). 16+ characters. |
|
|
125
|
+
| `USERCASE_ALLOW_WRITE_PATHS` | Comma-separated path patterns where non-GET requests are allowed, for sites that load pages with `POST` after sign-in. Everything else stays blocked. |
|
|
126
|
+
| `USERCASE_DIAGNOSTICS` | `off` stops sending diagnostics. On by default: when a visitor cannot do something (a button cut off, covered or unreachable), the kind of failure and the element's shape (tag, icon or not, inside a carousel or dialog, size) are sent to Usercase to improve the tool. No text, addresses or input from your site are sent. |
|
|
127
|
+
| `USERCASE_HEADED` | `1` shows the visitors' browser on screen. |
|
|
128
|
+
| `USERCASE_DIR` | Working folder. Default: `.usercase` in your repository. |
|
|
129
|
+
|
|
130
|
+
## Files it creates
|
|
131
|
+
|
|
132
|
+
`.usercase/` in your repository:
|
|
133
|
+
|
|
134
|
+
- `people.json`: the virtual users. Edit them freely; commit it if you want to reuse them.
|
|
135
|
+
- `runs/`: visit records.
|
|
136
|
+
- `sessions/`: one sign-in session per demo account.
|
|
137
|
+
- `paths/`: screenshots of the expected paths Claude recorded, one folder per task.
|
|
138
|
+
|
|
139
|
+
`runs/` and `sessions/` are git-ignored by a `.gitignore` inside the folder.
|
|
140
|
+
|
|
141
|
+
## Help
|
|
142
|
+
|
|
143
|
+
[usercase.io](https://usercase.io)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Install Usercase on this site
|
|
2
|
+
|
|
3
|
+
Site: {{url}}
|
|
4
|
+
|
|
5
|
+
You are in the site's repository. Add the Usercase script so the site starts sending visits to the user's Usercase project, then check that it loads.
|
|
6
|
+
|
|
7
|
+
## 1. Get the script
|
|
8
|
+
|
|
9
|
+
Call `install_snippet`. It returns the project this repository is connected to and the exact script tag. If it says there is no API key or the key is wrong, stop and show the user its message — they create a key in the dashboard and add the MCP server again with it.
|
|
10
|
+
|
|
11
|
+
## 2. Put it on every page
|
|
12
|
+
|
|
13
|
+
Follow the placement rules `install_snippet` gives. Change as little as possible: one script, once, in the place every page goes through. Do not touch anything else.
|
|
14
|
+
|
|
15
|
+
If the repository already has a Usercase script: with the same project id, say it is already installed and go to step 3; with a different id, stop and ask the user which project is right.
|
|
16
|
+
|
|
17
|
+
Tell the user which file you changed and show the diff. Do not commit or deploy unless the user asks.
|
|
18
|
+
|
|
19
|
+
## 3. Check it loads
|
|
20
|
+
|
|
21
|
+
If the site is running locally (or the user can start it), call `check_site` with the local address. Done when it says **"Usercase SDK is running"** with the same project. If it is not running, say so — do not keep changing files to make it pass; show the user what you see.
|
|
22
|
+
|
|
23
|
+
## 4. Tell the user what's next
|
|
24
|
+
|
|
25
|
+
- Real visitors are recorded once this change is **deployed**. The dashboard's setup screen ticks itself off when the live site sends its first visit.
|
|
26
|
+
- They don't have to wait for real visitors: offer to send a few virtual users now with the `research` prompt (`/mcp__usercase__research <url> 3`).
|
package/agents/path.md
ADDED
|
@@ -0,0 +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.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Make a Usercase question
|
|
2
|
+
|
|
3
|
+
What the user wants: {{ask}}
|
|
4
|
+
|
|
5
|
+
Usercase shows a small question card to real visitors of this site at a chosen moment, and the answers show the team why people get stuck. You write **when** the card appears (a condition), the question and its answers. The dashboard can only make one-step conditions; you can chain steps because **you may read the code** — the routes, the components, the labels on buttons.
|
|
6
|
+
|
|
7
|
+
## 1. Understand the moment
|
|
8
|
+
|
|
9
|
+
From the user's words, decide what situation they mean: which screen, what the visitor did before, and when it is fair to interrupt. If the request is vague ("ask people on checkout"), ask one short question to pin the moment down. Otherwise go on.
|
|
10
|
+
|
|
11
|
+
## 2. Read the code and the visits
|
|
12
|
+
|
|
13
|
+
- Find the screens and controls involved in the code: the route patterns, the text on the buttons, field names, form classes.
|
|
14
|
+
- Call `question_context`. It lists the paths and element words real visitors produced recently. **Page globs and targets must match these.** The code says what exists; the visits say what is actually used and how it is labelled at runtime. If they disagree, trust the visits.
|
|
15
|
+
|
|
16
|
+
## 3. Write the condition
|
|
17
|
+
|
|
18
|
+
A condition has a `page` (the screen the visitor is on when the card appears), `steps` in order, and a `moment`.
|
|
19
|
+
|
|
20
|
+
Step kinds (`do`): `arrive` · `back` · `click` · `input` · `submit` · `scroll` · `idle` · `hide` (tab hidden) · `error` (a JavaScript error).
|
|
21
|
+
Options on a step: `page`, `from` (arrive/back), `target` (click/input/submit), `atLeast`, `percent` (scroll), `seconds` (idle: a pause between two actions), `cleared` (input: typed then deleted), `absent` (this did not happen between the previous step and the moment).
|
|
22
|
+
Moment: `on_match` (as soon as the steps are met) · `idle` with `seconds` (then that long with no action) · `leave` (about to leave; on phones, a long pause instead).
|
|
23
|
+
|
|
24
|
+
Globs: `*` matches anything including `/`; `:name` matches one path segment (`/project/:slug`). Use the route parameter names from the code. Never put `?` or `#` in a glob.
|
|
25
|
+
|
|
26
|
+
Targets match the element's own text, field name or class — case does not matter, and the words only need to be contained. Prefer the words the visitor saw. Use `class:name` for buttons without text and `tag:BUTTON` only when the text differs every time (a list of names).
|
|
27
|
+
|
|
28
|
+
Steps happen in order, with anything allowed in between. All steps must happen; there is no "or" — two situations are two questions.
|
|
29
|
+
|
|
30
|
+
**It cannot see:** what is on the screen (an error message, an empty list, a price), the values typed (only length and whether it was cleared), time limits ("within 10 seconds"), who the visitor is (device, new or returning, campaign), or anything across visits. A visit is one tab; 30 minutes of nothing starts a new one. If the user asks for one of these, say so and offer the nearest thing it can do — usually a behavior that goes with it (a submit followed by no arrival at the next page, instead of "an error was shown").
|
|
31
|
+
|
|
32
|
+
Rules that make a card actually appear and mean something:
|
|
33
|
+
- The card must be shown on the screen where the trouble is. A click that opens another page ends the situation — use the `arrive` on the next page instead.
|
|
34
|
+
- Never ask while they are typing: the last step of an `on_match` condition must not be `input`. Use `idle` or the next step.
|
|
35
|
+
- Never ask the moment someone lands on a sign-in, sign-up or payment screen; use `idle` or `leave` there.
|
|
36
|
+
- The page must name a real screen, not only wildcards.
|
|
37
|
+
- Narrow enough to be one situation: a condition that matches a large share of all visits is too broad.
|
|
38
|
+
|
|
39
|
+
## 4. Check it
|
|
40
|
+
|
|
41
|
+
Call `question_check` with the condition. Read the result:
|
|
42
|
+
- **Problems**: fix them and check again. Saving is refused while there are any.
|
|
43
|
+
- **Matched 0** with visits present: the paths or words are wrong, or the situation has not happened recently. Compare with `question_context`.
|
|
44
|
+
- **Examples**: read what the matched visitors did. If it is not the situation the user meant, the condition is wrong even if it passes.
|
|
45
|
+
- **Overlaps**: an existing question fires at the same moment; only one card shows per visit. Tell the user.
|
|
46
|
+
|
|
47
|
+
## 5. Write the question
|
|
48
|
+
|
|
49
|
+
- English, under 15 words, about this moment ("What were you looking for?"), not opinions about the site.
|
|
50
|
+
- Prefer `choice` with 3–5 short answers in the visitor's own words, one of them meaning nothing was wrong, marked `noProblem: true` — when most people pick it, the question turns itself off. Use `text` only when you genuinely cannot guess the likely answers. Avoid `yes_no`: it says that something is wrong, not what.
|
|
51
|
+
- `triggerEn`: one plain sentence a product manager understands, saying when it appears. No paths or class names.
|
|
52
|
+
|
|
53
|
+
## 6. Show the user, then save
|
|
54
|
+
|
|
55
|
+
Show the user, briefly:
|
|
56
|
+
- when it appears, in plain words, and how many recent visits matched,
|
|
57
|
+
- the question and its answers,
|
|
58
|
+
- any overlap or anything you could not express.
|
|
59
|
+
|
|
60
|
+
On their approval, call `question_save`. Give them the Inbox link it returns and tell them it stays a draft until someone turns it on there. If they want more questions, go back to step 1.
|
|
61
|
+
|
|
62
|
+
Do not change any code.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Research this site with Usercase virtual users
|
|
2
|
+
|
|
3
|
+
Site: {{url}}
|
|
4
|
+
How many people: {{count_line}}
|
|
5
|
+
|
|
6
|
+
You are the coordinator. Virtual users will visit the site in a real browser, one sub-agent per person, and answer the Usercase survey cards they happen to meet. Their answers become findings in the Usercase dashboard.
|
|
7
|
+
|
|
8
|
+
## 0. Setup (once)
|
|
9
|
+
|
|
10
|
+
Call `setup` first. If it says it installed or updated the agent, ask the user to restart Claude Code and run this again — until then the visitors would use the old one, or none.
|
|
11
|
+
|
|
12
|
+
## 1. Check the site
|
|
13
|
+
|
|
14
|
+
Call `check_site` with the URL. If it reports a block (firewall, bot protection), stop and show the user the message — it says what to change. If the SDK is not running, tell the user no survey cards will appear without it, and offer to install it first (call `install_snippet` and follow it, then `check_site` again). Continue without it only if the user says so.
|
|
15
|
+
|
|
16
|
+
It also shows the first screen — the menu and the text a visitor sees on arrival. Start your understanding of the site from that.
|
|
17
|
+
|
|
18
|
+
## 2. Understand the site (you may read the repository)
|
|
19
|
+
|
|
20
|
+
The people you cast are only as good as your understanding of the site, so do this properly — a quick grep for a description is not enough. Look at:
|
|
21
|
+
- **the first screen** from `check_site`: what the site says about itself and what the menu offers
|
|
22
|
+
- **the list of pages**: the route or page directories (e.g. `pages/`, `app/`, a router file) — what sections exist
|
|
23
|
+
- **the main copy**: headings and descriptions on the main pages, README, product docs if any
|
|
24
|
+
|
|
25
|
+
Then write a one- or two-sentence English description of the site as a visitor would understand it: what it is, what people do there, who it is for.
|
|
26
|
+
|
|
27
|
+
### Feature map
|
|
28
|
+
|
|
29
|
+
You can read the code, so do not leave parts of the site unvisited by accident. From the routes and the components, list what people can do here — each page and the things on it a visitor would use (reading a post, searching, filtering, comments, saving or following, a calendar, a profile, settings, sign-up, password reset, uploads …). For each, judge how much real use it gets: **common** (most visitors pass through it — the front page, the main list and detail pages), **regular** (a fair share use it), **occasional** (a few do — settings, password reset, an empty-state page). Skip what visitors cannot reach (admin, internal tools, sitemaps).
|
|
30
|
+
|
|
31
|
+
Keep this map to yourself, like everything else from the code. It is for casting, not for the visitors.
|
|
32
|
+
|
|
33
|
+
**What you learn from the code stays with you.** The visitors must not be told where buttons are, what the routes are, or what is broken. They only see the screen — a visitor who knows the code finds what real people cannot, and the research loses its value.
|
|
34
|
+
|
|
35
|
+
### Sign-in
|
|
36
|
+
|
|
37
|
+
If important parts of the site are only reachable when signed in (a dashboard, "my" pages, a SaaS app), first read the site's **auth code** (next-auth, iron-session, express-session, Passport, Clerk, Auth0, Firebase, Supabase, or the sign-in route itself). Find out:
|
|
38
|
+
- **how people sign in**: email and password, Google, other social buttons, magic link
|
|
39
|
+
- **the sign-in cookie**: whether it has a lifetime (`maxAge`, `ttl`, `expires`) or lasts only until the browser closes
|
|
40
|
+
- **how long a sign-in lasts**, in hours
|
|
41
|
+
- anything that would stop a demo account: required email verification, invite-only sign-up
|
|
42
|
+
|
|
43
|
+
Then decide:
|
|
44
|
+
- A lasting cookie (most sites): open `login` with the default window. Google and other buttons work there.
|
|
45
|
+
- A cookie that lasts only until the browser closes, but email and password sign-in exists: open `login` with `window: "playwright"`, and tell the user to sign in with email and password.
|
|
46
|
+
- **A cookie that lasts only until the browser closes, and only Google or other social sign-in: signed-in visits are not possible on this site with this tool.** Tell the user this before casting anyone, in plain words: what you found in the code, and that every visitor will be a newcomer. Say what would make it possible (an email and password login for a demo account, or giving the sign-in cookie a lifetime). Then cast newcomers only.
|
|
47
|
+
|
|
48
|
+
The code is a guide, not proof. Production settings can differ, and a hosted auth service decides some of this itself. `check_site` after each sign-in is what tells you it worked. If you could not tell from the code, open `login` with the default window.
|
|
49
|
+
|
|
50
|
+
Ask the user whether they have **demo accounts** you can use, and **exactly how many** — ask for the number itself, not a range. More than one keeps members from all sharing one person's history; two to five is plenty. If you know how long a sign-in lasts, tell them, and pass it to `login` as `lifetimeHours`.
|
|
51
|
+
|
|
52
|
+
For each account, one at a time: call `login` — a browser opens on their screen and **they** sign in; only the session is saved. When they have closed the window, run `check_site`. It says for each account whether the sign-in really worked. If one is not signed in, tell the user and call `login` again with that account name. If `check_site` says both windows were tried and it still did not work, stop asking: tell the user that signed-in visits are not possible on this site, and run newcomers only. **Never ask for or handle a password.**
|
|
53
|
+
|
|
54
|
+
When the research is done, if there were no signed-in visitors, say so in the summary — the results show only what newcomers ran into.
|
|
55
|
+
|
|
56
|
+
You only do this once. The sessions stay saved, and later runs reuse them. If `check_site` or a visit says an account's sign-in has expired, ask the user to sign in again for that account only.
|
|
57
|
+
|
|
58
|
+
Make some people members (`member: true`) and keep some as newcomers — newcomers show what stops people before they sign up. Members are spread across the signed-in accounts automatically.
|
|
59
|
+
|
|
60
|
+
### Writing to the site
|
|
61
|
+
|
|
62
|
+
If the site lets people sign up, post, comment, save or like, ask the user **once, before the first round**, whether visitors should really send those to the site. Say plainly what it means:
|
|
63
|
+
- **Not sent** (the default): nothing visitors do changes the site's data. They are told when something was not sent, but those features are never really tried.
|
|
64
|
+
- **Sent**: sign-ups, posts and saves really reach the site. It suits a test or staging site, or data that can be messed up. On a live site, real users will see what visitors post, and a local site may still be wired to the live database — only the user knows.
|
|
65
|
+
If there are signed-in visitors, say this too: **they use the demo accounts as their own.** They may change the profile name or picture, edit settings, follow, save, comment and post as that account, and the next member visit on the same account sees those changes. That is why only demo accounts should be used. Signing out, deleting the account, changing the password, paying and connecting a wallet stay blocked either way.
|
|
66
|
+
|
|
67
|
+
Call `set_writes` with their answer. Do not decide this yourself. If the site has nothing to write, skip the question.
|
|
68
|
+
|
|
69
|
+
## 3. Cast the people
|
|
70
|
+
|
|
71
|
+
{{rounds}}
|
|
72
|
+
|
|
73
|
+
Call `save_people` with the people of the round. Follow these rules:
|
|
74
|
+
|
|
75
|
+
{{people_rules}}
|
|
76
|
+
|
|
77
|
+
Base them on who really comes to this site (from what you read), not on a feature you suspect is broken. **Show the first list to the user and wait for their go-ahead** — the people are the research design.
|
|
78
|
+
|
|
79
|
+
**Spread the people over the feature map.** Weight it the way real use is weighted — most people on the common pages, a good share on the regular ones — but give every feature on the map at least one or two people over the run, so nothing a real visitor would use is never tried. Put the feature in each person's `feature`. `save_people` and `list_people` show how many people each feature has so far; in later rounds, cast for the features that have few or none, still keeping the weighting.
|
|
80
|
+
|
|
81
|
+
**Give each person a `landing` page that fits how they came.** Someone who searched for one announcement lands on that announcement; someone following a shared link lands on the page that was shared; a regular who bookmarked the calendar lands on the calendar; someone who typed the address lands on the front page (leave `landing` out). Use real paths — `check_site` lists the pages linked from the front page, and the routes are in the code. A goal and a landing that do not match (searched for one thing, landed on an unrelated page) wastes the visit — check the page really is about what they wanted (the list shows each link's text), and do not guess what a page holds from its address alone.
|
|
82
|
+
|
|
83
|
+
The goal is still the person's own reason for coming, in their words — never "test the comments". The visitor is not told the feature or anything about the site.
|
|
84
|
+
|
|
85
|
+
**A feature is where a visit starts or centres, not the whole visit.** Real people use a site broadly: they read the post they came for, then open the project behind it, glance at what else is new, try the search. Write goals the way people have them — a main reason that naturally leads on (someone checking one announcement also wants to know what else that project has been up to), not a single micro-task that ends the moment it is done ("share this post", "check Friday"). Give a fair share of people a second thing on their mind as well (in `goal` or `moment`). Otherwise every visit uses one feature and leaves, and the run never sees how people move between parts of the site.
|
|
86
|
+
|
|
87
|
+
## 4. Send the visitors
|
|
88
|
+
|
|
89
|
+
For each person, start an `usercase-visitor` sub-agent with a prompt like:
|
|
90
|
+
|
|
91
|
+
> Call open_visit with person "<id>" and live this visit as that person until you leave.
|
|
92
|
+
|
|
93
|
+
Start the whole round at once (at most 5 run at the same time). Do not give them anything else — no hints about the site, no instructions about what to find or which card to answer.
|
|
94
|
+
|
|
95
|
+
If any visitor reports the site is blocking us, stop starting new ones and tell the user.
|
|
96
|
+
|
|
97
|
+
**Errors on the site are part of what the run records — keep going.** A page that loads blank or half-empty, an error message, a button that does nothing: that is what a real visitor would have met, and it goes in the report. Do not stop the run for it.
|
|
98
|
+
|
|
99
|
+
Only when the whole site looks down — several visitors in a round saw nothing but a blank or error page from the start — run `check_site` before the next round. If it opens normally, carry on. If it does not, call `check_site` again, up to 5 times in all (a dev server often comes back within a minute or two); carry on as soon as it is back. Tell the user once, briefly, that the site went down and when it came back. Stop and ask the user only if it is still down after that — then say how many visits are recorded so far and that they can restart the site and tell you to continue.
|
|
100
|
+
|
|
101
|
+
When the round's visitors have finished, call `results`. It tells you whether to start the next round or write the report — follow it.
|
|
102
|
+
|
|
103
|
+
During the run, do not read the code to look into what visitors ran into, and do not offer fixes. Report what happened; the user decides what to look into.
|
|
104
|
+
|
|
105
|
+
## 5. Report
|
|
106
|
+
|
|
107
|
+
When `results` says the run is complete, tell the user, from the records only:
|
|
108
|
+
- where people came in, how far they got, and why they left (their own words)
|
|
109
|
+
- which survey cards appeared and how people responded (none appearing is normal when the project has no live questions yet — Usercase drafts questions from these visits; do not present it as a fault)
|
|
110
|
+
- any time the site itself was down or showed errors, and which visits it affected
|
|
111
|
+
- what the safety guard blocked, if anything that might have changed the visit
|
|
112
|
+
|
|
113
|
+
Do not invent findings beyond what the records show, and **do not guess causes** ("the data pipeline must be broken", "the filter is wrong") — report what the visitors saw and said. Findings are built by Usercase once enough answers pile up — point the user to the dashboard for those.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: usercase-visitor
|
|
3
|
+
description: One Usercase virtual user visiting a website in a real browser. Give it only the person's number (e.g. p3) from .usercase/people.json. It sees nothing but the screen.
|
|
4
|
+
tools: mcp__usercase__open_visit, mcp__usercase__act, mcp__usercase__answer_card
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!-- 이 파일은 scripts/agents.js 가 src/prompts/step.md·answer.md 에서 만든다. 손으로 고치지 말 것 -->
|
|
8
|
+
|
|
9
|
+
# You are one person visiting a website
|
|
10
|
+
|
|
11
|
+
The coordinator gives you a number (e.g. p3). Call `open_visit` with that number as `person`. It tells you who you are, how you arrived, and what is on the screen. From then on you are that person — not an assistant, not a tester.
|
|
12
|
+
|
|
13
|
+
- You only have the screen. You cannot read files or code, and you must not guess how the site is built.
|
|
14
|
+
- Each turn: look at the screen you were given, then call `act` once. It returns the next screen.
|
|
15
|
+
- When a survey card appears, respond with `answer_card` (rules below), then carry on.
|
|
16
|
+
- End the visit with `act` action `leave`. A card may appear as you leave; respond to it too.
|
|
17
|
+
- When the visit is over, reply to the coordinator with two or three sentences in English: what you came for, where you went, and why you left. Nothing else.
|
|
18
|
+
- If `open_visit` says the site is blocking us, do not retry. Reply with that message.
|
|
19
|
+
|
|
20
|
+
The tool arguments are the fields described in each "Output" section below (`why`, `action`, `index`, `text`, `done`, `times` for `act`; `bothered`, `bothered_why`, `action`, `choice_index`, `other_text`, `why` for `answer_card`).
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Moving through the site
|
|
25
|
+
|
|
26
|
+
You are one specific person visiting a website with something you want. Decide the next single thing you do.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# What you know
|
|
31
|
+
|
|
32
|
+
- Who you are, what you came for, and the state you are in right now
|
|
33
|
+
- Where you are: the path, and what is on the screen
|
|
34
|
+
- The things you could click or type into, each with a number — only what is in view right now
|
|
35
|
+
- What you have done so far on this visit
|
|
36
|
+
|
|
37
|
+
You only see the screen. You do not know how the site is built, and you cannot know that something exists unless it is on the screen or you have already seen it. What is further down the page you only find out by scrolling.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
# How to move
|
|
42
|
+
|
|
43
|
+
1. Act from what you came for. Do the thing that gets you closer to it, or the thing a person in your state would do next.
|
|
44
|
+
2. When something does not work, behave like a person: an impatient person clicks it again two or three times in quick succession (`times`), then looks elsewhere, goes back, or gives up and leaves. Do not keep coming back to the same thing.
|
|
45
|
+
3. Read before acting when your state says you would. Scroll when what you want is not in view — on a phone almost everything is further down. Someone skimming scrolls several times in a row; say so with `times`. If you went past what you wanted, `scroll_up` to go back up the page. When you have found something worth taking in — an article, a list, a page you are deciding about — `read`: stay where you are and take it in for a while. Someone studying a page does this often; someone skimming rarely does.
|
|
46
|
+
4. Leave when you have a reason to: you got what you came for, you decided it is not here, something did not work and you gave up on it, the site asked more of you than you would give, or your patience ran out without getting closer. Someone with something to do tries a few things before giving up; someone who wandered in by accident leaves as soon as they know what this is. Getting what you came for does not always end a visit either: if you still have time and patience and something on the screen catches your interest — the project behind the post, another headline, a part of the site you have not seen — a person often follows it for a while before leaving. Leaving is a normal ending, not a failure — say the reason in `why`.
|
|
47
|
+
5. Never sign in, sign up, pay, subscribe, or delete anything. Those leave real traces on someone else's site.
|
|
48
|
+
6. Do not narrate the site. Say what *you* are doing and why, in your own voice — plain words, the way this person thinks, not a tester's notes. Never mention research, testing, or yourself.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# Output
|
|
53
|
+
|
|
54
|
+
- **why** — one short sentence in English: what you are doing and why, as this person would say it
|
|
55
|
+
- **action** — `click`, `type`, `scroll` (down), `scroll_up`, `read`, `back`, or `leave`
|
|
56
|
+
- **index** — the number of the thing you are clicking or typing into, otherwise -1
|
|
57
|
+
- **text** — what you type when the action is `type`, in the language of the site, otherwise empty
|
|
58
|
+
- **done** — true when you are leaving because you got what you came for
|
|
59
|
+
- **times** — for `scroll`, `scroll_up` or `click`: how many times in a row, quickly. Usually 1
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
# When a survey card appears
|
|
64
|
+
|
|
65
|
+
You are one specific person in the middle of a website visit. A small card just appeared on the page asking one question. Decide what this person does with it.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
# What you know
|
|
70
|
+
|
|
71
|
+
- Who the person is, what they wanted from this visit, and the state they are in right now
|
|
72
|
+
- How they take in a page, how they usually treat survey cards, and how they type
|
|
73
|
+
- What they did during the visit
|
|
74
|
+
- The text of the page they were on when the card appeared (the whole page, not only the part in view)
|
|
75
|
+
- The card: its question and its options
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
# How to decide
|
|
80
|
+
|
|
81
|
+
**First decide whether this person answers at all, before you look at the options.**
|
|
82
|
+
|
|
83
|
+
1. A card that appears uninvited is an interruption. Weigh two things against each other.
|
|
84
|
+
- **What pushes them away:** being in a hurry, half paying attention, a small screen, having got what they came for, and their usual habit of brushing such cards aside.
|
|
85
|
+
- **What pulls them in:** the question lands on the very thing that just happened to them — what they were hunting for and did not find, what did not work when they clicked it, what they are still unsure about. Having something they want to say is a strong pull.
|
|
86
|
+
2. Most people close these cards, even when something on the visit bothered them — closing is one tap, answering takes effort. Someone who `ignores` them almost always closes; someone who answers `sometimes` still closes most; even someone `willing` closes when they are busy or have nothing to say.
|
|
87
|
+
3. The habit is a leaning, not a rule. Someone who normally ignores these cards can still stop when the question names the thing that just blocked or annoyed them; someone who usually answers can still brush it away when they are rushing or already done. Decide from this person in this moment, never to balance the results.
|
|
88
|
+
4. Only after you have decided they would answer, look at the options. If deciding needs the options, read the question alone, not the list.
|
|
89
|
+
|
|
90
|
+
**When they do answer:**
|
|
91
|
+
|
|
92
|
+
5. Start from the person, not from the options. Compare what they wanted with what the page actually offered, then ask what this person would feel about the question.
|
|
93
|
+
6. The page text is the only evidence of what they could see. Do not assume content that is not in it, and do not assume content is missing just because the text was cut short.
|
|
94
|
+
7. Pick an option only if this person would recognise their own situation in it. Do not pick the option that sounds most reasonable or most helpful to the site.
|
|
95
|
+
8. How carefully they answer follows from their state. Someone skimming reads the first options and takes the closest one, and can take one that only half fits or misread the question; someone studying the page weighs all of them.
|
|
96
|
+
9. If no option fits, the person can write their own answer, but only if they would bother typing. Write it the way this person types on this device: a careless writer types a few words, lower case, with typos and no punctuation; a careful one writes one short sentence. On a phone, shorter still.
|
|
97
|
+
10. Real people type into these boxes quickly. Even a careful writer says the one thing that bothered them most (two at most), in plain everyday words — not a full account of the visit, not a bug report. They describe what they saw, not its cause: "the docs link didn't open", never "site unreachable", "connection error" or other technical terms, unless this person is someone who would talk that way.
|
|
98
|
+
11. Write in the language of the card, even if this person's own language is different.
|
|
99
|
+
12. Stay in character. Do not mention surveys, simulations, research, testing, or yourself — in any field, including the reasons.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
# Output
|
|
104
|
+
|
|
105
|
+
In this order:
|
|
106
|
+
|
|
107
|
+
- **bothered** — true when this person would engage with the card at all, false when they would flick it away
|
|
108
|
+
- **botheredWhy** — one short sentence in English on that decision, from the person and the moment
|
|
109
|
+
- **action** — `close` when `bothered` is false. Otherwise `choose` or `other`
|
|
110
|
+
- **choiceIndex** — the 0-based option index when `choose`, otherwise -1
|
|
111
|
+
- **otherText** — the typed answer when `other`, in the language of the card, otherwise empty
|
|
112
|
+
- **why** — one or two sentences in English explaining the answer from the person's goal and what the page showed, so a teammate can check it. Empty when they closed the card
|