@usercase/mcp 0.2.5-next.0 → 0.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/agents/install.md +26 -26
- package/agents/research.md +115 -115
- package/mcp/server.js +95 -95
- package/package.json +2 -2
package/agents/install.md
CHANGED
|
@@ -1,26 +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 (`{{research_cmd}} <url> 3`).
|
|
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 (`{{research_cmd}} <url> 3`).
|
package/agents/research.md
CHANGED
|
@@ -1,115 +1,115 @@
|
|
|
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
|
-
{{focus}}
|
|
9
|
-
|
|
10
|
-
## 0. Setup (once)
|
|
11
|
-
|
|
12
|
-
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.
|
|
13
|
-
|
|
14
|
-
## 1. Check the site
|
|
15
|
-
|
|
16
|
-
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.
|
|
17
|
-
|
|
18
|
-
It also shows the first screen — the menu and the text a visitor sees on arrival. Start your understanding of the site from that.
|
|
19
|
-
|
|
20
|
-
## 2. Understand the site (you may read the repository)
|
|
21
|
-
|
|
22
|
-
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:
|
|
23
|
-
- **the first screen** from `check_site`: what the site says about itself and what the menu offers
|
|
24
|
-
- **the list of pages**: the route or page directories (e.g. `pages/`, `app/`, a router file) — what sections exist
|
|
25
|
-
- **the main copy**: headings and descriptions on the main pages, README, product docs if any
|
|
26
|
-
|
|
27
|
-
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.
|
|
28
|
-
|
|
29
|
-
### Feature map
|
|
30
|
-
|
|
31
|
-
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).
|
|
32
|
-
|
|
33
|
-
Keep this map to yourself, like everything else from the code. It is for casting, not for the visitors.
|
|
34
|
-
|
|
35
|
-
**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.
|
|
36
|
-
|
|
37
|
-
### Sign-in
|
|
38
|
-
|
|
39
|
-
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:
|
|
40
|
-
- **how people sign in**: email and password, Google, other social buttons, magic link
|
|
41
|
-
- **the sign-in cookie**: whether it has a lifetime (`maxAge`, `ttl`, `expires`) or lasts only until the browser closes
|
|
42
|
-
- **how long a sign-in lasts**, in hours
|
|
43
|
-
- anything that would stop a demo account: required email verification, invite-only sign-up
|
|
44
|
-
|
|
45
|
-
Then decide:
|
|
46
|
-
- A lasting cookie (most sites): open `login` with the default window. Google and other buttons work there.
|
|
47
|
-
- 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.
|
|
48
|
-
- **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.
|
|
49
|
-
|
|
50
|
-
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.
|
|
51
|
-
|
|
52
|
-
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`.
|
|
53
|
-
|
|
54
|
-
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.**
|
|
55
|
-
|
|
56
|
-
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.
|
|
57
|
-
|
|
58
|
-
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.
|
|
59
|
-
|
|
60
|
-
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.
|
|
61
|
-
|
|
62
|
-
### Writing to the site
|
|
63
|
-
|
|
64
|
-
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:
|
|
65
|
-
- **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.
|
|
66
|
-
- **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.
|
|
67
|
-
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.
|
|
68
|
-
|
|
69
|
-
Call `set_writes` with their answer. Do not decide this yourself. If the site has nothing to write, skip the question.
|
|
70
|
-
|
|
71
|
-
## 3. Cast the people
|
|
72
|
-
|
|
73
|
-
{{rounds}}
|
|
74
|
-
|
|
75
|
-
Call `save_people` with the people of the round. Follow these rules:
|
|
76
|
-
|
|
77
|
-
{{people_rules}}
|
|
78
|
-
|
|
79
|
-
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.
|
|
80
|
-
|
|
81
|
-
**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.
|
|
82
|
-
|
|
83
|
-
**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.
|
|
84
|
-
|
|
85
|
-
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.
|
|
86
|
-
|
|
87
|
-
**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.
|
|
88
|
-
|
|
89
|
-
## 4. Send the visitors
|
|
90
|
-
|
|
91
|
-
For each person, start a `{{visitor}}` sub-agent with a prompt like:
|
|
92
|
-
|
|
93
|
-
> Call open_visit with person "<id>" and live this visit as that person until you leave.
|
|
94
|
-
|
|
95
|
-
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.
|
|
96
|
-
|
|
97
|
-
If any visitor reports the site is blocking us, stop starting new ones and tell the user.
|
|
98
|
-
|
|
99
|
-
**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.
|
|
100
|
-
|
|
101
|
-
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.
|
|
102
|
-
|
|
103
|
-
When the round's visitors have finished, call `results`. It tells you whether to start the next round or write the report — follow it.
|
|
104
|
-
|
|
105
|
-
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.
|
|
106
|
-
|
|
107
|
-
## 5. Report
|
|
108
|
-
|
|
109
|
-
When `results` says the run is complete, tell the user, from the records only:
|
|
110
|
-
- where people came in, how far they got, and why they left (their own words)
|
|
111
|
-
- 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)
|
|
112
|
-
- any time the site itself was down or showed errors, and which visits it affected
|
|
113
|
-
- what the safety guard blocked, if anything that might have changed the visit
|
|
114
|
-
|
|
115
|
-
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.
|
|
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
|
+
{{focus}}
|
|
9
|
+
|
|
10
|
+
## 0. Setup (once)
|
|
11
|
+
|
|
12
|
+
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.
|
|
13
|
+
|
|
14
|
+
## 1. Check the site
|
|
15
|
+
|
|
16
|
+
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.
|
|
17
|
+
|
|
18
|
+
It also shows the first screen — the menu and the text a visitor sees on arrival. Start your understanding of the site from that.
|
|
19
|
+
|
|
20
|
+
## 2. Understand the site (you may read the repository)
|
|
21
|
+
|
|
22
|
+
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:
|
|
23
|
+
- **the first screen** from `check_site`: what the site says about itself and what the menu offers
|
|
24
|
+
- **the list of pages**: the route or page directories (e.g. `pages/`, `app/`, a router file) — what sections exist
|
|
25
|
+
- **the main copy**: headings and descriptions on the main pages, README, product docs if any
|
|
26
|
+
|
|
27
|
+
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.
|
|
28
|
+
|
|
29
|
+
### Feature map
|
|
30
|
+
|
|
31
|
+
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).
|
|
32
|
+
|
|
33
|
+
Keep this map to yourself, like everything else from the code. It is for casting, not for the visitors.
|
|
34
|
+
|
|
35
|
+
**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.
|
|
36
|
+
|
|
37
|
+
### Sign-in
|
|
38
|
+
|
|
39
|
+
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:
|
|
40
|
+
- **how people sign in**: email and password, Google, other social buttons, magic link
|
|
41
|
+
- **the sign-in cookie**: whether it has a lifetime (`maxAge`, `ttl`, `expires`) or lasts only until the browser closes
|
|
42
|
+
- **how long a sign-in lasts**, in hours
|
|
43
|
+
- anything that would stop a demo account: required email verification, invite-only sign-up
|
|
44
|
+
|
|
45
|
+
Then decide:
|
|
46
|
+
- A lasting cookie (most sites): open `login` with the default window. Google and other buttons work there.
|
|
47
|
+
- 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.
|
|
48
|
+
- **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.
|
|
49
|
+
|
|
50
|
+
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.
|
|
51
|
+
|
|
52
|
+
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`.
|
|
53
|
+
|
|
54
|
+
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.**
|
|
55
|
+
|
|
56
|
+
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.
|
|
57
|
+
|
|
58
|
+
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.
|
|
59
|
+
|
|
60
|
+
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.
|
|
61
|
+
|
|
62
|
+
### Writing to the site
|
|
63
|
+
|
|
64
|
+
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:
|
|
65
|
+
- **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.
|
|
66
|
+
- **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.
|
|
67
|
+
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.
|
|
68
|
+
|
|
69
|
+
Call `set_writes` with their answer. Do not decide this yourself. If the site has nothing to write, skip the question.
|
|
70
|
+
|
|
71
|
+
## 3. Cast the people
|
|
72
|
+
|
|
73
|
+
{{rounds}}
|
|
74
|
+
|
|
75
|
+
Call `save_people` with the people of the round. Follow these rules:
|
|
76
|
+
|
|
77
|
+
{{people_rules}}
|
|
78
|
+
|
|
79
|
+
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.
|
|
80
|
+
|
|
81
|
+
**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.
|
|
82
|
+
|
|
83
|
+
**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.
|
|
84
|
+
|
|
85
|
+
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.
|
|
86
|
+
|
|
87
|
+
**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.
|
|
88
|
+
|
|
89
|
+
## 4. Send the visitors
|
|
90
|
+
|
|
91
|
+
For each person, start a `{{visitor}}` sub-agent with a prompt like:
|
|
92
|
+
|
|
93
|
+
> Call open_visit with person "<id>" and live this visit as that person until you leave.
|
|
94
|
+
|
|
95
|
+
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.
|
|
96
|
+
|
|
97
|
+
If any visitor reports the site is blocking us, stop starting new ones and tell the user.
|
|
98
|
+
|
|
99
|
+
**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.
|
|
100
|
+
|
|
101
|
+
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.
|
|
102
|
+
|
|
103
|
+
When the round's visitors have finished, call `results`. It tells you whether to start the next round or write the report — follow it.
|
|
104
|
+
|
|
105
|
+
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.
|
|
106
|
+
|
|
107
|
+
## 5. Report
|
|
108
|
+
|
|
109
|
+
When `results` says the run is complete, tell the user, from the records only:
|
|
110
|
+
- where people came in, how far they got, and why they left (their own words)
|
|
111
|
+
- 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)
|
|
112
|
+
- any time the site itself was down or showed errors, and which visits it affected
|
|
113
|
+
- what the safety guard blocked, if anything that might have changed the visit
|
|
114
|
+
|
|
115
|
+
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.
|
package/mcp/server.js
CHANGED
|
@@ -667,101 +667,101 @@ function runStatus() {
|
|
|
667
667
|
].join("\n");
|
|
668
668
|
}
|
|
669
669
|
|
|
670
|
-
const doc = (name) => fs.readFileSync(path.join(__dirname, "..", "agents", name), "utf8").replace(/\r\n/g, "\n");
|
|
671
|
-
/** 조건 쓰는 규칙은 question·steps 가 같이 쓴다 — 한 벌만 둔다 */
|
|
672
|
-
const agentDoc = (name, vars = {}) => {
|
|
673
|
-
let body = doc(name).replace("{{condition_rules}}", doc("condition.md").trim());
|
|
674
|
-
for (const [k, v] of Object.entries(vars)) body = body.replace(`{{${k}}}`, v);
|
|
675
|
-
return body;
|
|
676
|
-
};
|
|
677
|
-
|
|
678
|
-
/**
|
|
679
|
-
* 명령 지시문. 따로 쓰면 MCP 프롬프트(/mcp__usercase__research), 플러그인이면 instructions 도구(/usercase:research 스킬이 부른다)로
|
|
680
|
-
* 같은 글이 나간다
|
|
681
|
-
*/
|
|
682
|
-
const COMMANDS = {
|
|
683
|
-
research: {
|
|
684
|
-
description: "Research this site with Usercase virtual users",
|
|
685
|
-
argsSchema: {
|
|
686
|
-
url: z.string().optional(),
|
|
687
|
-
count: z.string().optional().describe('How many people (default 5), or "unlimited" to keep going until you stop it'),
|
|
688
|
-
focus: z.string().optional().describe("Only people who come for this part of the site, e.g. calendar, or comments and sharing"),
|
|
689
|
-
},
|
|
690
|
-
body: ({ url, count, focus }) => {
|
|
691
|
-
const plan = researchPlan(count);
|
|
692
|
-
run = { total: plan.total, since: Date.now() };
|
|
693
|
-
// 사람 만드는 규칙은 우리 AI 로 만들 때와 같은 원본(people.md)에서 가져온다 — 두 벌로 두지 않는다.
|
|
694
|
-
// 자유 탐방이라 원본이 말하는 "path" 는 "자기 목적으로 왔다" 뿐이다
|
|
695
|
-
const rules =
|
|
696
|
-
"(Here the \"path\" is simply: came to the site with something of their own to find out.)\n\n" +
|
|
697
|
-
fs
|
|
698
|
-
.readFileSync(path.join(__dirname, "..", "src", "prompts", "people.md"), "utf8")
|
|
699
|
-
.replace(/\r\n/g, "\n")
|
|
700
|
-
.replace(/^# Role:.*\n+/, "")
|
|
701
|
-
.replace(/^#/gm, "###");
|
|
702
|
-
// focus 가 있으면 그 기능에 올 사람만 — 바뀌는 단계만 따로 적은 글을 앞에 붙인다. 방문자는 모른다
|
|
703
|
-
const f = (focus || "").trim();
|
|
704
|
-
const focusPart = f ? doc("focus.md").replaceAll("{{focus}}", f).trim() + "\n\n" : "";
|
|
705
|
-
return doc("research.md")
|
|
706
|
-
.replace("{{focus}}\n", focusPart)
|
|
707
|
-
.replace("{{url}}", url || "(ask the user)")
|
|
708
|
-
.replace("{{count_line}}", plan.line)
|
|
709
|
-
.replace("{{rounds}}", plan.rounds)
|
|
710
|
-
.replace("{{people_rules}}", rules)
|
|
711
|
-
.replaceAll("{{visitor}}", VISITOR);
|
|
712
|
-
},
|
|
713
|
-
},
|
|
714
|
-
install: {
|
|
715
|
-
description: "Install the Usercase script on this site from its repository",
|
|
716
|
-
argsSchema: { url: z.string().optional() },
|
|
717
|
-
body: ({ url }) =>
|
|
718
|
-
doc("install.md")
|
|
719
|
-
.replace("{{url}}", url || "(the site in this repository)")
|
|
720
|
-
.replace("{{research_cmd}}", cmd("research")),
|
|
721
|
-
},
|
|
722
|
-
path: {
|
|
723
|
-
description: "Record the expected path of a Usercase research's tasks",
|
|
724
|
-
// 대시보드 과제 화면의 명령은 과제 id 를 붙인다 — 그 과제만, 경로가 있어도 다시
|
|
725
|
-
argsSchema: { research: z.string().optional(), task: z.string().optional() },
|
|
726
|
-
body: ({ research, task }) =>
|
|
727
|
-
doc("path.md")
|
|
728
|
-
.replace("{{research}}", research || "(ask the user — the dashboard shows it)")
|
|
729
|
-
.replace("{{task}}", task ? `Task: ${task} — record this task only, and record it again even if it already has a path.` : "Task: all tasks of the research"),
|
|
730
|
-
},
|
|
731
|
-
// 인자를 두지 않는다 — Claude Code 는 명령어 인자를 공백으로 나눠 문장을 못 싣는다. 무엇을 물을지는 대화로 묻는다
|
|
732
|
-
question: {
|
|
733
|
-
description: "Make a Usercase question for a precise moment on your site — Claude suggests the moment, question and answers",
|
|
734
|
-
body: () => agentDoc("question.md"),
|
|
735
|
-
},
|
|
736
|
-
steps: {
|
|
737
|
-
description: "Write the steps for the question open in your dashboard (New question → Several steps)",
|
|
738
|
-
argsSchema: { session: z.string().optional().describe("The window id shown in the dashboard command") },
|
|
739
|
-
body: ({ session }) => agentDoc("steps.md", { session: session || "(missing — ask the user to copy the command from the dashboard)" }),
|
|
740
|
-
},
|
|
741
|
-
};
|
|
742
|
-
|
|
743
|
-
if (PLUGIN) {
|
|
744
|
-
server.registerTool(
|
|
745
|
-
"instructions",
|
|
746
|
-
{
|
|
747
|
-
description:
|
|
748
|
-
"The step-by-step instructions for a Usercase command (research, install, path, question, steps). The plugin's /usercase:* commands call this; follow what it returns as the user's request.",
|
|
749
|
-
inputSchema: {
|
|
750
|
-
command: z.enum(Object.keys(COMMANDS)),
|
|
751
|
-
...COMMANDS.research.argsSchema,
|
|
752
|
-
...COMMANDS.path.argsSchema,
|
|
753
|
-
...COMMANDS.steps.argsSchema,
|
|
754
|
-
},
|
|
755
|
-
},
|
|
756
|
-
wrap(async ({ command, ...args }) => text(COMMANDS[command].body(args))),
|
|
757
|
-
);
|
|
758
|
-
} else {
|
|
759
|
-
for (const [name, c] of Object.entries(COMMANDS)) {
|
|
760
|
-
const meta = c.argsSchema ? { description: c.description, argsSchema: c.argsSchema } : { description: c.description };
|
|
761
|
-
server.registerPrompt(name, meta, (args = {}) => ({ messages: [{ role: "user", content: { type: "text", text: c.body(args) } }] }));
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
670
|
+
const doc = (name) => fs.readFileSync(path.join(__dirname, "..", "agents", name), "utf8").replace(/\r\n/g, "\n");
|
|
671
|
+
/** 조건 쓰는 규칙은 question·steps 가 같이 쓴다 — 한 벌만 둔다 */
|
|
672
|
+
const agentDoc = (name, vars = {}) => {
|
|
673
|
+
let body = doc(name).replace("{{condition_rules}}", doc("condition.md").trim());
|
|
674
|
+
for (const [k, v] of Object.entries(vars)) body = body.replace(`{{${k}}}`, v);
|
|
675
|
+
return body;
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* 명령 지시문. 따로 쓰면 MCP 프롬프트(/mcp__usercase__research), 플러그인이면 instructions 도구(/usercase:research 스킬이 부른다)로
|
|
680
|
+
* 같은 글이 나간다
|
|
681
|
+
*/
|
|
682
|
+
const COMMANDS = {
|
|
683
|
+
research: {
|
|
684
|
+
description: "Research this site with Usercase virtual users",
|
|
685
|
+
argsSchema: {
|
|
686
|
+
url: z.string().optional(),
|
|
687
|
+
count: z.string().optional().describe('How many people (default 5), or "unlimited" to keep going until you stop it'),
|
|
688
|
+
focus: z.string().optional().describe("Only people who come for this part of the site, e.g. calendar, or comments and sharing"),
|
|
689
|
+
},
|
|
690
|
+
body: ({ url, count, focus }) => {
|
|
691
|
+
const plan = researchPlan(count);
|
|
692
|
+
run = { total: plan.total, since: Date.now() };
|
|
693
|
+
// 사람 만드는 규칙은 우리 AI 로 만들 때와 같은 원본(people.md)에서 가져온다 — 두 벌로 두지 않는다.
|
|
694
|
+
// 자유 탐방이라 원본이 말하는 "path" 는 "자기 목적으로 왔다" 뿐이다
|
|
695
|
+
const rules =
|
|
696
|
+
"(Here the \"path\" is simply: came to the site with something of their own to find out.)\n\n" +
|
|
697
|
+
fs
|
|
698
|
+
.readFileSync(path.join(__dirname, "..", "src", "prompts", "people.md"), "utf8")
|
|
699
|
+
.replace(/\r\n/g, "\n")
|
|
700
|
+
.replace(/^# Role:.*\n+/, "")
|
|
701
|
+
.replace(/^#/gm, "###");
|
|
702
|
+
// focus 가 있으면 그 기능에 올 사람만 — 바뀌는 단계만 따로 적은 글을 앞에 붙인다. 방문자는 모른다
|
|
703
|
+
const f = (focus || "").trim();
|
|
704
|
+
const focusPart = f ? doc("focus.md").replaceAll("{{focus}}", f).trim() + "\n\n" : "";
|
|
705
|
+
return doc("research.md")
|
|
706
|
+
.replace("{{focus}}\n", focusPart)
|
|
707
|
+
.replace("{{url}}", url || "(ask the user)")
|
|
708
|
+
.replace("{{count_line}}", plan.line)
|
|
709
|
+
.replace("{{rounds}}", plan.rounds)
|
|
710
|
+
.replace("{{people_rules}}", rules)
|
|
711
|
+
.replaceAll("{{visitor}}", VISITOR);
|
|
712
|
+
},
|
|
713
|
+
},
|
|
714
|
+
install: {
|
|
715
|
+
description: "Install the Usercase script on this site from its repository",
|
|
716
|
+
argsSchema: { url: z.string().optional() },
|
|
717
|
+
body: ({ url }) =>
|
|
718
|
+
doc("install.md")
|
|
719
|
+
.replace("{{url}}", url || "(the site in this repository)")
|
|
720
|
+
.replace("{{research_cmd}}", cmd("research")),
|
|
721
|
+
},
|
|
722
|
+
path: {
|
|
723
|
+
description: "Record the expected path of a Usercase research's tasks",
|
|
724
|
+
// 대시보드 과제 화면의 명령은 과제 id 를 붙인다 — 그 과제만, 경로가 있어도 다시
|
|
725
|
+
argsSchema: { research: z.string().optional(), task: z.string().optional() },
|
|
726
|
+
body: ({ research, task }) =>
|
|
727
|
+
doc("path.md")
|
|
728
|
+
.replace("{{research}}", research || "(ask the user — the dashboard shows it)")
|
|
729
|
+
.replace("{{task}}", task ? `Task: ${task} — record this task only, and record it again even if it already has a path.` : "Task: all tasks of the research"),
|
|
730
|
+
},
|
|
731
|
+
// 인자를 두지 않는다 — Claude Code 는 명령어 인자를 공백으로 나눠 문장을 못 싣는다. 무엇을 물을지는 대화로 묻는다
|
|
732
|
+
question: {
|
|
733
|
+
description: "Make a Usercase question for a precise moment on your site — Claude suggests the moment, question and answers",
|
|
734
|
+
body: () => agentDoc("question.md"),
|
|
735
|
+
},
|
|
736
|
+
steps: {
|
|
737
|
+
description: "Write the steps for the question open in your dashboard (New question → Several steps)",
|
|
738
|
+
argsSchema: { session: z.string().optional().describe("The window id shown in the dashboard command") },
|
|
739
|
+
body: ({ session }) => agentDoc("steps.md", { session: session || "(missing — ask the user to copy the command from the dashboard)" }),
|
|
740
|
+
},
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
if (PLUGIN) {
|
|
744
|
+
server.registerTool(
|
|
745
|
+
"instructions",
|
|
746
|
+
{
|
|
747
|
+
description:
|
|
748
|
+
"The step-by-step instructions for a Usercase command (research, install, path, question, steps). The plugin's /usercase:* commands call this; follow what it returns as the user's request.",
|
|
749
|
+
inputSchema: {
|
|
750
|
+
command: z.enum(Object.keys(COMMANDS)),
|
|
751
|
+
...COMMANDS.research.argsSchema,
|
|
752
|
+
...COMMANDS.path.argsSchema,
|
|
753
|
+
...COMMANDS.steps.argsSchema,
|
|
754
|
+
},
|
|
755
|
+
},
|
|
756
|
+
wrap(async ({ command, ...args }) => text(COMMANDS[command].body(args))),
|
|
757
|
+
);
|
|
758
|
+
} else {
|
|
759
|
+
for (const [name, c] of Object.entries(COMMANDS)) {
|
|
760
|
+
const meta = c.argsSchema ? { description: c.description, argsSchema: c.argsSchema } : { description: c.description };
|
|
761
|
+
server.registerPrompt(name, meta, (args = {}) => ({ messages: [{ role: "user", content: { type: "text", text: c.body(args) } }] }));
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
765
|
async function main() {
|
|
766
766
|
// 예전에 깐 방문자 에이전트가 있으면 이 버전 것으로 바꿔 둔다 — setup 은 처음 한 번만 불려서 옛 지시문이 남는다.
|
|
767
767
|
// 리서치를 한 번도 안 쓴 사람에게는 만들지 않는다
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usercase/mcp",
|
|
3
|
-
"version": "0.2.5
|
|
3
|
+
"version": "0.2.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",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
|
-
"check": "node --check mcp/server.js && node scripts/onboarding.js --check && node scripts/agents.js --check",
|
|
28
|
+
"check": "node --check mcp/server.js && node scripts/onboarding.js --check && node scripts/agents.js --check && node scripts/plugin.js --check",
|
|
29
29
|
"docs": "node scripts/onboarding.js && node scripts/agents.js",
|
|
30
30
|
"release:next": "node scripts/release.js next",
|
|
31
31
|
"release:promote": "node scripts/release.js promote",
|