userflow-mcp 0.2.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/LICENSE +21 -0
- package/README.md +177 -0
- package/dist/analysis/clarity.d.ts +7 -0
- package/dist/analysis/clarity.d.ts.map +1 -0
- package/dist/analysis/clarity.js +114 -0
- package/dist/analysis/clarity.js.map +1 -0
- package/dist/analysis/cognitive-load.d.ts +7 -0
- package/dist/analysis/cognitive-load.d.ts.map +1 -0
- package/dist/analysis/cognitive-load.js +45 -0
- package/dist/analysis/cognitive-load.js.map +1 -0
- package/dist/analysis/emotional-arc.d.ts +10 -0
- package/dist/analysis/emotional-arc.d.ts.map +1 -0
- package/dist/analysis/emotional-arc.js +95 -0
- package/dist/analysis/emotional-arc.js.map +1 -0
- package/dist/analysis/friction.d.ts +23 -0
- package/dist/analysis/friction.d.ts.map +1 -0
- package/dist/analysis/friction.js +61 -0
- package/dist/analysis/friction.js.map +1 -0
- package/dist/feedback/comparison.d.ts +11 -0
- package/dist/feedback/comparison.d.ts.map +1 -0
- package/dist/feedback/comparison.js +156 -0
- package/dist/feedback/comparison.js.map +1 -0
- package/dist/feedback/generator.d.ts +19 -0
- package/dist/feedback/generator.d.ts.map +1 -0
- package/dist/feedback/generator.js +139 -0
- package/dist/feedback/generator.js.map +1 -0
- package/dist/feedback/report.d.ts +10 -0
- package/dist/feedback/report.d.ts.map +1 -0
- package/dist/feedback/report.js +123 -0
- package/dist/feedback/report.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/personas/engine.d.ts +39 -0
- package/dist/personas/engine.d.ts.map +1 -0
- package/dist/personas/engine.js +58 -0
- package/dist/personas/engine.js.map +1 -0
- package/dist/personas/presets.d.ts +11 -0
- package/dist/personas/presets.d.ts.map +1 -0
- package/dist/personas/presets.js +251 -0
- package/dist/personas/presets.js.map +1 -0
- package/dist/personas/types.d.ts +11 -0
- package/dist/personas/types.d.ts.map +1 -0
- package/dist/personas/types.js +23 -0
- package/dist/personas/types.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +288 -0
- package/dist/server.js.map +1 -0
- package/dist/session/session-manager.d.ts +54 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +228 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/types.d.ts +35 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +2 -0
- package/dist/session/types.js.map +1 -0
- package/dist/types.d.ts +284 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/actions.d.ts +17 -0
- package/dist/utils/actions.d.ts.map +1 -0
- package/dist/utils/actions.js +89 -0
- package/dist/utils/actions.js.map +1 -0
- package/dist/utils/browser.d.ts +22 -0
- package/dist/utils/browser.d.ts.map +1 -0
- package/dist/utils/browser.js +122 -0
- package/dist/utils/browser.js.map +1 -0
- package/dist/utils/page-snapshot.d.ts +11 -0
- package/dist/utils/page-snapshot.d.ts.map +1 -0
- package/dist/utils/page-snapshot.js +102 -0
- package/dist/utils/page-snapshot.js.map +1 -0
- package/dist/walker/action-planner.d.ts +15 -0
- package/dist/walker/action-planner.d.ts.map +1 -0
- package/dist/walker/action-planner.js +236 -0
- package/dist/walker/action-planner.js.map +1 -0
- package/dist/walker/flow-walker.d.ts +10 -0
- package/dist/walker/flow-walker.d.ts.map +1 -0
- package/dist/walker/flow-walker.js +107 -0
- package/dist/walker/flow-walker.js.map +1 -0
- package/dist/walker/session-recorder.d.ts +28 -0
- package/dist/walker/session-recorder.d.ts.map +1 -0
- package/dist/walker/session-recorder.js +90 -0
- package/dist/walker/session-recorder.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
export const PERSONA_PRESETS = [
|
|
2
|
+
{
|
|
3
|
+
id: "preset-alex-first-timer",
|
|
4
|
+
name: "Alex",
|
|
5
|
+
description: "The First-Timer",
|
|
6
|
+
background: "Alex is a 22-year-old college student who has never used a SaaS product before. They mostly use social media apps and are trying a productivity tool for the first time after a friend recommended it. They don't know what 'onboarding' or 'dashboard' means and will likely feel overwhelmed by too many options.",
|
|
7
|
+
goals: [
|
|
8
|
+
"Figure out what the app actually does",
|
|
9
|
+
"Try to create an account / sign up",
|
|
10
|
+
"Complete one basic action to see if it's useful",
|
|
11
|
+
],
|
|
12
|
+
traits: {
|
|
13
|
+
techLiteracy: "novice",
|
|
14
|
+
patience: "moderate",
|
|
15
|
+
ageGroup: "young_adult",
|
|
16
|
+
devicePreference: "mobile",
|
|
17
|
+
accessibilityNeeds: ["none"],
|
|
18
|
+
domainKnowledge: 1,
|
|
19
|
+
attentionToDetail: 3,
|
|
20
|
+
},
|
|
21
|
+
behaviorNotes: [
|
|
22
|
+
"Reads headings but skips paragraphs of text",
|
|
23
|
+
"Taps on things that look colorful or animated",
|
|
24
|
+
"Gets confused by jargon like 'workspace' or 'integration'",
|
|
25
|
+
"Will try the back button if something feels wrong",
|
|
26
|
+
"Expects things to work like Instagram or TikTok",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "preset-morgan-power-user",
|
|
31
|
+
name: "Morgan",
|
|
32
|
+
description: "The Power User",
|
|
33
|
+
background: "Morgan is a senior software engineer with 12 years of experience. They evaluate tools by how fast they can get to the advanced features and whether the product respects power users. They will immediately look for keyboard shortcuts, API docs, and CLI options. If the product feels dumbed down, they'll leave.",
|
|
34
|
+
goals: [
|
|
35
|
+
"Find advanced features and configuration options",
|
|
36
|
+
"Test keyboard shortcuts and navigation efficiency",
|
|
37
|
+
"Evaluate whether an API or developer tools exist",
|
|
38
|
+
"Assess the product's technical depth",
|
|
39
|
+
],
|
|
40
|
+
traits: {
|
|
41
|
+
techLiteracy: "expert",
|
|
42
|
+
patience: "low",
|
|
43
|
+
ageGroup: "adult",
|
|
44
|
+
devicePreference: "desktop",
|
|
45
|
+
accessibilityNeeds: ["none"],
|
|
46
|
+
domainKnowledge: 9,
|
|
47
|
+
attentionToDetail: 9,
|
|
48
|
+
},
|
|
49
|
+
behaviorNotes: [
|
|
50
|
+
"Opens DevTools immediately to inspect network requests",
|
|
51
|
+
"Tries keyboard shortcuts like Cmd+K, Ctrl+/, Esc",
|
|
52
|
+
"Looks for a /docs or /api route",
|
|
53
|
+
"Gets annoyed by forced tutorials or tooltips",
|
|
54
|
+
"Judges loading speed and checks for unnecessary re-renders",
|
|
55
|
+
"Will inspect the tech stack via source and headers",
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "preset-patricia-senior-explorer",
|
|
60
|
+
name: "Patricia",
|
|
61
|
+
description: "The Senior Explorer",
|
|
62
|
+
background: "Patricia is a 68-year-old retired teacher who uses her desktop computer daily for email and online shopping. Her daughter told her about this app. She wears reading glasses and prefers larger text. She's willing to spend time learning but needs clear instructions and gets anxious when she can't find a way back to where she started.",
|
|
63
|
+
goals: [
|
|
64
|
+
"Understand what the product does in simple terms",
|
|
65
|
+
"Find help, support, or a phone number to call",
|
|
66
|
+
"Complete one basic task successfully",
|
|
67
|
+
"Feel confident she won't break anything",
|
|
68
|
+
],
|
|
69
|
+
traits: {
|
|
70
|
+
techLiteracy: "basic",
|
|
71
|
+
patience: "high",
|
|
72
|
+
ageGroup: "senior",
|
|
73
|
+
devicePreference: "desktop",
|
|
74
|
+
accessibilityNeeds: ["low_vision"],
|
|
75
|
+
domainKnowledge: 2,
|
|
76
|
+
attentionToDetail: 7,
|
|
77
|
+
},
|
|
78
|
+
behaviorNotes: [
|
|
79
|
+
"Reads everything carefully before clicking",
|
|
80
|
+
"Looks for a 'Help' or 'Contact Us' link first",
|
|
81
|
+
"Avoids anything that looks like it might cost money",
|
|
82
|
+
"Prefers large, clearly labeled buttons",
|
|
83
|
+
"Gets anxious when a page changes unexpectedly",
|
|
84
|
+
"May not notice elements in the footer or behind hamburger menus",
|
|
85
|
+
"Will zoom in the browser if text is too small",
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: "preset-jordan-busy-executive",
|
|
90
|
+
name: "Jordan",
|
|
91
|
+
description: "The Busy Executive",
|
|
92
|
+
background: "Jordan is a 45-year-old VP of Operations checking out a tool someone on their team suggested. They're reviewing it on their phone between meetings. They have about 30 seconds to decide if this is worth their time. If the value proposition isn't immediately clear, they'll close the tab and tell their team to find something else.",
|
|
93
|
+
goals: [
|
|
94
|
+
"Understand the value proposition in under 10 seconds",
|
|
95
|
+
"Find pricing information immediately",
|
|
96
|
+
"Make a go/no-go decision quickly",
|
|
97
|
+
"Determine if there's an enterprise plan or sales contact",
|
|
98
|
+
],
|
|
99
|
+
traits: {
|
|
100
|
+
techLiteracy: "intermediate",
|
|
101
|
+
patience: "very_low",
|
|
102
|
+
ageGroup: "middle_aged",
|
|
103
|
+
devicePreference: "mobile",
|
|
104
|
+
accessibilityNeeds: ["none"],
|
|
105
|
+
domainKnowledge: 6,
|
|
106
|
+
attentionToDetail: 4,
|
|
107
|
+
},
|
|
108
|
+
behaviorNotes: [
|
|
109
|
+
"Scans only the hero section and maybe one scroll",
|
|
110
|
+
"Looks for pricing link in the nav within 3 seconds",
|
|
111
|
+
"Will not read anything longer than two sentences",
|
|
112
|
+
"Trusts logos of known companies (social proof)",
|
|
113
|
+
"Immediately leaves if there's a paywall before seeing value",
|
|
114
|
+
"Wants to see ROI or time-saved claims front and center",
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "preset-sam-accessibility-tester",
|
|
119
|
+
name: "Sam",
|
|
120
|
+
description: "The Accessibility Tester",
|
|
121
|
+
background: "Sam is a 35-year-old UX consultant who is blind and uses a screen reader (NVDA/VoiceOver) for all computer interactions. They navigate entirely by keyboard using Tab, Enter, Arrow keys, and screen reader shortcuts. They evaluate products for WCAG compliance and will immediately notice missing alt text, broken focus order, or unlabeled form fields.",
|
|
122
|
+
goals: [
|
|
123
|
+
"Navigate the entire interface using only keyboard",
|
|
124
|
+
"Verify screen reader compatibility and meaningful announcements",
|
|
125
|
+
"Check that all interactive elements have accessible labels",
|
|
126
|
+
"Complete core user flows without sighted assistance",
|
|
127
|
+
],
|
|
128
|
+
traits: {
|
|
129
|
+
techLiteracy: "advanced",
|
|
130
|
+
patience: "moderate",
|
|
131
|
+
ageGroup: "adult",
|
|
132
|
+
devicePreference: "desktop",
|
|
133
|
+
accessibilityNeeds: ["screen_reader"],
|
|
134
|
+
domainKnowledge: 8,
|
|
135
|
+
attentionToDetail: 10,
|
|
136
|
+
},
|
|
137
|
+
behaviorNotes: [
|
|
138
|
+
"Navigates exclusively via Tab, Shift+Tab, Enter, Space, and arrow keys",
|
|
139
|
+
"Listens for ARIA landmarks and heading structure to build a mental map",
|
|
140
|
+
"Will try to skip to main content using screen reader shortcuts",
|
|
141
|
+
"Notes every image without alt text and every button without a label",
|
|
142
|
+
"Checks that modal dialogs trap focus correctly",
|
|
143
|
+
"Verifies that dynamic content changes are announced",
|
|
144
|
+
"Tests color contrast only when sighted colleagues assist",
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "preset-riley-skeptical-evaluator",
|
|
149
|
+
name: "Riley",
|
|
150
|
+
description: "The Skeptical Evaluator",
|
|
151
|
+
background: "Riley is a 32-year-old product manager who has been burned by overpromising SaaS products before. They're comparing three competing tools and will scrutinize every claim. They look for social proof, transparent pricing, and real customer reviews. If anything feels manipulative (fake urgency, hidden fees), they'll flag it and likely choose a competitor.",
|
|
152
|
+
goals: [
|
|
153
|
+
"Compare this product's claims against competitors",
|
|
154
|
+
"Find genuine social proof (reviews, case studies, testimonials)",
|
|
155
|
+
"Look for red flags (dark patterns, hidden costs, fake urgency)",
|
|
156
|
+
"Verify pricing transparency and check for hidden fees",
|
|
157
|
+
],
|
|
158
|
+
traits: {
|
|
159
|
+
techLiteracy: "intermediate",
|
|
160
|
+
patience: "moderate",
|
|
161
|
+
ageGroup: "adult",
|
|
162
|
+
devicePreference: "desktop",
|
|
163
|
+
accessibilityNeeds: ["none"],
|
|
164
|
+
domainKnowledge: 7,
|
|
165
|
+
attentionToDetail: 8,
|
|
166
|
+
},
|
|
167
|
+
behaviorNotes: [
|
|
168
|
+
"Scrolls through the entire page before interacting",
|
|
169
|
+
"Looks for a pricing page and reads every line of fine print",
|
|
170
|
+
"Checks if testimonials link to real people or companies",
|
|
171
|
+
"Notices dark patterns like pre-checked boxes or misleading CTAs",
|
|
172
|
+
"Opens competitor tabs side by side for comparison",
|
|
173
|
+
"Distrusts 'limited time offer' banners and countdown timers",
|
|
174
|
+
"Searches for the company on review sites like G2 or Capterra",
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: "preset-casey-international-user",
|
|
179
|
+
name: "Casey",
|
|
180
|
+
description: "The International User",
|
|
181
|
+
background: "Casey is a 28-year-old graphic designer based in Seoul who speaks English as a second language. They can read English reasonably well but struggle with idioms, slang, and culturally specific references. They prefer apps with clean visual design over text-heavy interfaces and will look for language/region settings.",
|
|
182
|
+
goals: [
|
|
183
|
+
"Understand the product despite potential language barriers",
|
|
184
|
+
"Find localization or language settings",
|
|
185
|
+
"Complete the core signup and onboarding flow",
|
|
186
|
+
"Determine if the product works well in their region",
|
|
187
|
+
],
|
|
188
|
+
traits: {
|
|
189
|
+
techLiteracy: "basic",
|
|
190
|
+
patience: "high",
|
|
191
|
+
ageGroup: "adult",
|
|
192
|
+
devicePreference: "mobile",
|
|
193
|
+
accessibilityNeeds: ["none"],
|
|
194
|
+
domainKnowledge: 4,
|
|
195
|
+
attentionToDetail: 6,
|
|
196
|
+
},
|
|
197
|
+
behaviorNotes: [
|
|
198
|
+
"Relies heavily on icons, images, and visual cues over text",
|
|
199
|
+
"Looks for a globe or language icon in the header/footer",
|
|
200
|
+
"Gets confused by idiomatic phrases like 'hit the ground running'",
|
|
201
|
+
"Prefers short, simple sentences over marketing copy",
|
|
202
|
+
"Checks whether prices are shown in local currency",
|
|
203
|
+
"May use browser translation if no native language option exists",
|
|
204
|
+
"Notices if dates, numbers, or currencies use the wrong format",
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
id: "preset-taylor-return-visitor",
|
|
209
|
+
name: "Taylor",
|
|
210
|
+
description: "The Return Visitor",
|
|
211
|
+
background: "Taylor is a 26-year-old freelance writer who used this product six months ago and is coming back to use a specific feature they remember. They don't want to re-learn the interface. They expect things to be where they left them and will be frustrated if the UI has changed significantly. They also want to see what's new since they last visited.",
|
|
212
|
+
goals: [
|
|
213
|
+
"Find a specific feature they used before",
|
|
214
|
+
"Check what's new or changed since last visit",
|
|
215
|
+
"Complete a repeat task as quickly as possible",
|
|
216
|
+
"Determine if the product has improved enough to keep using",
|
|
217
|
+
],
|
|
218
|
+
traits: {
|
|
219
|
+
techLiteracy: "advanced",
|
|
220
|
+
patience: "moderate",
|
|
221
|
+
ageGroup: "young_adult",
|
|
222
|
+
devicePreference: "desktop",
|
|
223
|
+
accessibilityNeeds: ["none"],
|
|
224
|
+
domainKnowledge: 6,
|
|
225
|
+
attentionToDetail: 5,
|
|
226
|
+
},
|
|
227
|
+
behaviorNotes: [
|
|
228
|
+
"Goes directly to where they remember the feature being",
|
|
229
|
+
"Gets frustrated if navigation has been reorganized",
|
|
230
|
+
"Looks for a 'What's New' or changelog section",
|
|
231
|
+
"Skips onboarding/tutorials assuming they already know the basics",
|
|
232
|
+
"Uses search or Cmd+K to find things quickly",
|
|
233
|
+
"Compares current experience to their memory of the old one",
|
|
234
|
+
"Will notice and comment on any UI regressions",
|
|
235
|
+
],
|
|
236
|
+
},
|
|
237
|
+
];
|
|
238
|
+
/**
|
|
239
|
+
* Find a preset persona by display name (case-insensitive).
|
|
240
|
+
*/
|
|
241
|
+
export function getPresetByName(name) {
|
|
242
|
+
const normalized = name.toLowerCase();
|
|
243
|
+
return PERSONA_PRESETS.find((p) => p.name.toLowerCase() === normalized);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Find a preset persona by its unique id.
|
|
247
|
+
*/
|
|
248
|
+
export function getPresetById(id) {
|
|
249
|
+
return PERSONA_PRESETS.find((p) => p.id === id);
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=presets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.js","sourceRoot":"","sources":["../../src/personas/presets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type { Persona, PersonaTraits, TechLiteracy, PatienceLevel, AgeGroup, DevicePreference, AccessibilityNeed } from "../types.js";
|
|
2
|
+
/** Maximum steps a persona will take before giving up, based on patience. */
|
|
3
|
+
export declare const PATIENCE_STEP_LIMITS: Readonly<Record<string, number>>;
|
|
4
|
+
/** How likely (0-1) a persona is to notice non-obvious UI elements based on tech literacy. */
|
|
5
|
+
export declare const DISCOVERY_PROBABILITY: Readonly<Record<string, number>>;
|
|
6
|
+
/** Viewport dimensions by device preference. */
|
|
7
|
+
export declare const DEVICE_VIEWPORTS: Readonly<Record<string, {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
}>>;
|
|
11
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/personas/types.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEtI,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMjE,CAAC;AAEF,8FAA8F;AAC9F,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMlE,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAIxF,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Maximum steps a persona will take before giving up, based on patience. */
|
|
2
|
+
export const PATIENCE_STEP_LIMITS = {
|
|
3
|
+
very_low: 5,
|
|
4
|
+
low: 8,
|
|
5
|
+
moderate: 12,
|
|
6
|
+
high: 18,
|
|
7
|
+
very_high: 25,
|
|
8
|
+
};
|
|
9
|
+
/** How likely (0-1) a persona is to notice non-obvious UI elements based on tech literacy. */
|
|
10
|
+
export const DISCOVERY_PROBABILITY = {
|
|
11
|
+
novice: 0.2,
|
|
12
|
+
basic: 0.4,
|
|
13
|
+
intermediate: 0.6,
|
|
14
|
+
advanced: 0.8,
|
|
15
|
+
expert: 0.95,
|
|
16
|
+
};
|
|
17
|
+
/** Viewport dimensions by device preference. */
|
|
18
|
+
export const DEVICE_VIEWPORTS = {
|
|
19
|
+
mobile: { width: 375, height: 812 },
|
|
20
|
+
tablet: { width: 768, height: 1024 },
|
|
21
|
+
desktop: { width: 1440, height: 900 },
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/personas/types.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,oBAAoB,GAAqC;IACpE,QAAQ,EAAE,CAAC;IACX,GAAG,EAAE,CAAC;IACN,QAAQ,EAAE,EAAE;IACZ,IAAI,EAAE,EAAE;IACR,SAAS,EAAE,EAAE;CACd,CAAC;AAEF,8FAA8F;AAC9F,MAAM,CAAC,MAAM,qBAAqB,GAAqC;IACrE,MAAM,EAAE,GAAG;IACX,KAAK,EAAE,GAAG;IACV,YAAY,EAAE,GAAG;IACjB,QAAQ,EAAE,GAAG;IACb,MAAM,EAAE,IAAI;CACb,CAAC;AAEF,gDAAgD;AAChD,MAAM,CAAC,MAAM,gBAAgB,GAAgE;IAC3F,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;IACnC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IACpC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;CACtC,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA8BpE,wBAAgB,YAAY,IAAI,SAAS,CA0UxC"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { sessionManager } from "./session/session-manager.js";
|
|
4
|
+
import { walkFlow } from "./walker/flow-walker.js";
|
|
5
|
+
import { resolvePersona, PERSONA_PRESETS, listPresetNames } from "./personas/engine.js";
|
|
6
|
+
import { generateSessionReport } from "./feedback/generator.js";
|
|
7
|
+
import { comparePersonaSessions, generateComparisonReport } from "./feedback/comparison.js";
|
|
8
|
+
import { extractPageSnapshot } from "./utils/page-snapshot.js";
|
|
9
|
+
import { createPage, navigateAndWait, closePage } from "./utils/browser.js";
|
|
10
|
+
/**
|
|
11
|
+
* Serialize a PageSnapshot for text output (everything except the screenshot binary).
|
|
12
|
+
*/
|
|
13
|
+
function serializePageState(page) {
|
|
14
|
+
return JSON.stringify({
|
|
15
|
+
url: page.url,
|
|
16
|
+
title: page.title,
|
|
17
|
+
timestamp: page.timestamp,
|
|
18
|
+
loadTimeMs: page.loadTimeMs,
|
|
19
|
+
headings: page.headings,
|
|
20
|
+
mainText: page.mainText,
|
|
21
|
+
buttons: page.buttons.map((b) => ({ selector: b.selector, text: b.text, visible: b.isVisible })),
|
|
22
|
+
links: page.links.filter((l) => l.isVisible).slice(0, 30).map((l) => ({ selector: l.selector, text: l.text, href: l.href })),
|
|
23
|
+
formFields: page.formFields.map((f) => ({ selector: f.selector, type: f.type, placeholder: f.placeholder, ariaLabel: f.ariaLabel })),
|
|
24
|
+
errorMessages: page.errorMessages,
|
|
25
|
+
interactiveElementCount: page.interactiveElements.length,
|
|
26
|
+
}, null, 2);
|
|
27
|
+
}
|
|
28
|
+
export function createServer() {
|
|
29
|
+
const server = new McpServer({
|
|
30
|
+
name: "userflow-mcp",
|
|
31
|
+
version: "0.2.0",
|
|
32
|
+
});
|
|
33
|
+
// ═══════════════════════════════════════════════════════════════
|
|
34
|
+
// STEP-BY-STEP TOOLS (Claude drives the simulation)
|
|
35
|
+
// ═══════════════════════════════════════��═══════════════════════
|
|
36
|
+
// ── start_session ──────────────────────────────────────────────
|
|
37
|
+
server.tool("start_session", `Start a user flow session. Opens a browser, navigates to the URL, and returns the first page snapshot with a screenshot. Claude then drives the simulation step-by-step using the "step" tool, roleplaying as the persona.
|
|
38
|
+
|
|
39
|
+
Workflow: start_session → step (repeat) → end_session
|
|
40
|
+
|
|
41
|
+
The persona definition is returned so you can roleplay as them — adopt their tech literacy, patience, goals, and behavioral patterns when deciding what to click next.`, {
|
|
42
|
+
url: z.string().url().describe("The URL to start the session at"),
|
|
43
|
+
persona: z.string().optional().describe("Persona name (e.g., 'Alex', 'Morgan'). Use list_personas to see all. Omit to be persona-free."),
|
|
44
|
+
viewport_width: z.number().optional().describe("Override viewport width"),
|
|
45
|
+
viewport_height: z.number().optional().describe("Override viewport height"),
|
|
46
|
+
}, async ({ url, persona: personaName, viewport_width, viewport_height }) => {
|
|
47
|
+
const viewport = viewport_width || viewport_height
|
|
48
|
+
? { width: viewport_width, height: viewport_height }
|
|
49
|
+
: undefined;
|
|
50
|
+
const result = await sessionManager.createSession(url, personaName ?? undefined, viewport);
|
|
51
|
+
const personaBrief = result.persona
|
|
52
|
+
? [
|
|
53
|
+
`## Persona: ${result.persona.name}`,
|
|
54
|
+
`**${result.persona.description}**`,
|
|
55
|
+
``,
|
|
56
|
+
`> ${result.persona.background}`,
|
|
57
|
+
``,
|
|
58
|
+
`**Goals:** ${result.persona.goals.join(", ")}`,
|
|
59
|
+
`**Traits:** Tech: ${result.persona.traits.techLiteracy} | Patience: ${result.persona.traits.patience} | Age: ${result.persona.traits.ageGroup} | Device: ${result.persona.traits.devicePreference}`,
|
|
60
|
+
`**Accessibility:** ${result.persona.traits.accessibilityNeeds.join(", ")}`,
|
|
61
|
+
result.persona.behaviorNotes.length > 0 ? `**Behavior notes:** ${result.persona.behaviorNotes.join("; ")}` : "",
|
|
62
|
+
].filter(Boolean).join("\n")
|
|
63
|
+
: "No persona selected — you decide how to navigate.";
|
|
64
|
+
const pageState = serializePageState(result.page);
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
text: [
|
|
70
|
+
`# Session Started`,
|
|
71
|
+
`**Session ID:** ${result.sessionId}`,
|
|
72
|
+
`**URL:** ${result.page.url}`,
|
|
73
|
+
`**Title:** ${result.page.title}`,
|
|
74
|
+
``,
|
|
75
|
+
personaBrief,
|
|
76
|
+
``,
|
|
77
|
+
`## Page State`,
|
|
78
|
+
pageState,
|
|
79
|
+
``,
|
|
80
|
+
`---`,
|
|
81
|
+
`Use the **step** tool with this session_id to navigate. Look at the screenshot, think as this persona, and decide what to do next.`,
|
|
82
|
+
].join("\n"),
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: "image",
|
|
86
|
+
data: result.page.screenshot,
|
|
87
|
+
mimeType: "image/png",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
// ── step ────────���──────────────────────────────────────────────
|
|
93
|
+
server.tool("step", `Execute one step in a session. Tell the MCP what action to take (click, type, scroll, etc.) and optionally record your persona thoughts, emotional state, and any friction you noticed. Returns the resulting page state with a new screenshot.
|
|
94
|
+
|
|
95
|
+
Actions: click, type, scroll, scroll_up, navigate, select, hover, press_key, wait`, {
|
|
96
|
+
session_id: z.string().describe("Session ID from start_session"),
|
|
97
|
+
action: z.enum(["click", "type", "scroll", "scroll_up", "navigate", "select", "hover", "press_key", "wait"])
|
|
98
|
+
.describe("Action to perform"),
|
|
99
|
+
target: z.string().optional()
|
|
100
|
+
.describe("CSS selector (required for click, type, select, hover)"),
|
|
101
|
+
value: z.string().optional()
|
|
102
|
+
.describe("Text to type, URL to navigate to, option value, or key name"),
|
|
103
|
+
scroll_amount: z.number().optional()
|
|
104
|
+
.describe("Pixels to scroll (default: 500)"),
|
|
105
|
+
thought: z.string().optional()
|
|
106
|
+
.describe("Your persona's thought at this moment — recorded in the session transcript"),
|
|
107
|
+
emotional_state: z.enum(["curious", "confident", "neutral", "confused", "frustrated", "delighted", "anxious", "bored"]).optional()
|
|
108
|
+
.describe("How the persona feels right now"),
|
|
109
|
+
friction: z.array(z.object({
|
|
110
|
+
severity: z.enum(["low", "medium", "high", "critical"]),
|
|
111
|
+
description: z.string(),
|
|
112
|
+
suggestion: z.string(),
|
|
113
|
+
})).optional()
|
|
114
|
+
.describe("Friction points you noticed at this step"),
|
|
115
|
+
}, async ({ session_id, action, target, value, scroll_amount, thought, emotional_state, friction }) => {
|
|
116
|
+
const result = await sessionManager.executeStep(session_id, {
|
|
117
|
+
action,
|
|
118
|
+
target,
|
|
119
|
+
value,
|
|
120
|
+
scrollAmount: scroll_amount,
|
|
121
|
+
thought,
|
|
122
|
+
emotionalState: emotional_state,
|
|
123
|
+
frictionNotes: friction,
|
|
124
|
+
});
|
|
125
|
+
const pageState = serializePageState(result.page);
|
|
126
|
+
const statusLine = result.success
|
|
127
|
+
? `**Step ${result.stepIndex}:** ${action}${target ? ` → \`${target}\`` : ""} ✅`
|
|
128
|
+
: `**Step ${result.stepIndex}:** ${action}${target ? ` → \`${target}\`` : ""} ❌ ${result.error}`;
|
|
129
|
+
return {
|
|
130
|
+
content: [
|
|
131
|
+
{
|
|
132
|
+
type: "text",
|
|
133
|
+
text: [
|
|
134
|
+
statusLine,
|
|
135
|
+
`**URL:** ${result.page.url}`,
|
|
136
|
+
`**Title:** ${result.page.title}`,
|
|
137
|
+
``,
|
|
138
|
+
`## Page State`,
|
|
139
|
+
pageState,
|
|
140
|
+
].join("\n"),
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
type: "image",
|
|
144
|
+
data: result.page.screenshot,
|
|
145
|
+
mimeType: "image/png",
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
// ── end_session ────────────────────────────────────────────────
|
|
151
|
+
server.tool("end_session", "End a session and get the final report. Closes the browser, computes friction score and emotional arc, and returns the full session transcript with all recorded thoughts, friction points, and recommendations.", {
|
|
152
|
+
session_id: z.string().describe("Session ID to close"),
|
|
153
|
+
goal_achieved: z.boolean().optional().describe("Did the persona achieve their goal?"),
|
|
154
|
+
summary: z.string().optional().describe("Your overall summary of the session"),
|
|
155
|
+
}, async ({ session_id, goal_achieved, summary }) => {
|
|
156
|
+
const session = await sessionManager.endSession(session_id, goal_achieved, summary);
|
|
157
|
+
const report = generateSessionReport(session);
|
|
158
|
+
return {
|
|
159
|
+
content: [{ type: "text", text: report }],
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
// ── get_page_state ─────────────────────────────────────────────
|
|
163
|
+
server.tool("get_page_state", "Get the current page state and screenshot without performing any action. Useful for re-examining the page or checking if lazy-loaded content has appeared.", {
|
|
164
|
+
session_id: z.string().describe("Session ID"),
|
|
165
|
+
full_page: z.boolean().optional().describe("Capture full page instead of viewport (default: false)"),
|
|
166
|
+
}, async ({ session_id, full_page }) => {
|
|
167
|
+
const snapshot = await sessionManager.getPageState(session_id, full_page);
|
|
168
|
+
const pageState = serializePageState(snapshot);
|
|
169
|
+
return {
|
|
170
|
+
content: [
|
|
171
|
+
{ type: "text", text: pageState },
|
|
172
|
+
{ type: "image", data: snapshot.screenshot, mimeType: "image/png" },
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
// ═══════════════════════════════════════════════════════════════
|
|
177
|
+
// QUICK TOOLS (stateless, single-page)
|
|
178
|
+
// ═════════════���═════════════════════════════════════════════════
|
|
179
|
+
// ── quick_scan ─────────────────────────────────────────────────
|
|
180
|
+
server.tool("quick_scan", "Fast single-page scan. Navigates to the URL, captures a screenshot, extracts all interactive elements, headings, forms, and error messages. Returns everything to you for analysis — no session state required.", {
|
|
181
|
+
url: z.string().url().describe("URL to scan"),
|
|
182
|
+
viewport_width: z.number().optional().describe("Viewport width (default: 1440)"),
|
|
183
|
+
viewport_height: z.number().optional().describe("Viewport height (default: 900)"),
|
|
184
|
+
full_page: z.boolean().optional().describe("Capture full page (default: false)"),
|
|
185
|
+
wait_ms: z.number().optional().describe("Extra wait after page load in ms (default: 1000)"),
|
|
186
|
+
}, async ({ url, viewport_width, viewport_height, full_page, wait_ms }) => {
|
|
187
|
+
const width = viewport_width ?? 1440;
|
|
188
|
+
const height = viewport_height ?? 900;
|
|
189
|
+
const page = await createPage(width, height, 2);
|
|
190
|
+
try {
|
|
191
|
+
await navigateAndWait(page, url, wait_ms ?? 1000);
|
|
192
|
+
const snapshot = await extractPageSnapshot(page, { fullPage: full_page });
|
|
193
|
+
const pageState = serializePageState(snapshot);
|
|
194
|
+
return {
|
|
195
|
+
content: [
|
|
196
|
+
{ type: "text", text: `# Page Scan: ${snapshot.title}\n**URL:** ${url}\n**Load time:** ${snapshot.loadTimeMs}ms\n\n${pageState}` },
|
|
197
|
+
{ type: "image", data: snapshot.screenshot, mimeType: "image/png" },
|
|
198
|
+
],
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
await closePage(page);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
// ── list_personas ───────────────────────────────────���──────────
|
|
206
|
+
server.tool("list_personas", "List all built-in personas with their full trait definitions. Use this to choose a persona for start_session, or to understand each persona before roleplaying as them.", {}, async () => {
|
|
207
|
+
const personas = PERSONA_PRESETS.map((p) => ({
|
|
208
|
+
id: p.id,
|
|
209
|
+
name: p.name,
|
|
210
|
+
description: p.description,
|
|
211
|
+
background: p.background,
|
|
212
|
+
goals: p.goals,
|
|
213
|
+
traits: p.traits,
|
|
214
|
+
behaviorNotes: p.behaviorNotes,
|
|
215
|
+
}));
|
|
216
|
+
const markdown = PERSONA_PRESETS.map((p) => [
|
|
217
|
+
`### ${p.name}`,
|
|
218
|
+
`*${p.description}*`,
|
|
219
|
+
`> ${p.background}`,
|
|
220
|
+
`- **Tech:** ${p.traits.techLiteracy} | **Patience:** ${p.traits.patience} | **Device:** ${p.traits.devicePreference}`,
|
|
221
|
+
`- **Goals:** ${p.goals.join(", ")}`,
|
|
222
|
+
`- **Accessibility:** ${p.traits.accessibilityNeeds.join(", ")}`,
|
|
223
|
+
p.behaviorNotes.length > 0 ? `- **Notes:** ${p.behaviorNotes.join("; ")}` : "",
|
|
224
|
+
``,
|
|
225
|
+
].filter(Boolean).join("\n")).join("\n");
|
|
226
|
+
return {
|
|
227
|
+
content: [{
|
|
228
|
+
type: "text",
|
|
229
|
+
text: `# Available Personas\n\n${markdown}\n\n---\n\`\`\`json\n${JSON.stringify(personas, null, 2)}\n\`\`\``,
|
|
230
|
+
}],
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
// ═══════════════════════════════════════════════════════════════
|
|
234
|
+
// AUTO TOOLS (legacy heuristic walker — fast fallback)
|
|
235
|
+
// ══════════════════════════════════���════════════════════════════
|
|
236
|
+
// ── auto_walk ──────────────────────────────────────────────────
|
|
237
|
+
server.tool("auto_walk", "Fast automated walk using heuristic-based navigation (no AI reasoning). Runs the full flow autonomously and returns a report. Use this for quick scans when you don't need to drive step-by-step.", {
|
|
238
|
+
url: z.string().url().describe("Starting URL"),
|
|
239
|
+
persona: z.string().describe("Persona name (e.g., 'Alex', 'Morgan')"),
|
|
240
|
+
max_steps: z.number().optional().describe("Maximum steps (default: based on persona patience)"),
|
|
241
|
+
}, async ({ url, persona: personaName, max_steps }) => {
|
|
242
|
+
const persona = resolvePersona(personaName);
|
|
243
|
+
if (!persona) {
|
|
244
|
+
return {
|
|
245
|
+
content: [{ type: "text", text: `Persona "${personaName}" not found. Available: ${listPresetNames().join(", ")}` }],
|
|
246
|
+
isError: true,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const session = await walkFlow(url, persona, { maxSteps: max_steps });
|
|
250
|
+
const report = generateSessionReport(session);
|
|
251
|
+
const content = [
|
|
252
|
+
{ type: "text", text: report },
|
|
253
|
+
];
|
|
254
|
+
const firstScreenshot = session.steps[0]?.page.screenshot;
|
|
255
|
+
if (firstScreenshot) {
|
|
256
|
+
content.push({ type: "image", data: firstScreenshot, mimeType: "image/png" });
|
|
257
|
+
}
|
|
258
|
+
return { content };
|
|
259
|
+
});
|
|
260
|
+
// ── compare_personas_auto ──────────────────────────────────────
|
|
261
|
+
server.tool("compare_personas_auto", "Run the automated heuristic walker for multiple personas on the same URL and compare their experiences. Fast but less nuanced than step-by-step simulation.", {
|
|
262
|
+
url: z.string().url().describe("URL to test"),
|
|
263
|
+
personas: z.array(z.string()).min(2).max(5).describe("Persona names to compare (2-5)"),
|
|
264
|
+
max_steps: z.number().optional().describe("Max steps per persona"),
|
|
265
|
+
}, async ({ url, personas: personaNames, max_steps }) => {
|
|
266
|
+
const resolvedPersonas = [];
|
|
267
|
+
for (const name of personaNames) {
|
|
268
|
+
const persona = resolvePersona(name);
|
|
269
|
+
if (!persona) {
|
|
270
|
+
return {
|
|
271
|
+
content: [{ type: "text", text: `Persona "${name}" not found. Available: ${listPresetNames().join(", ")}` }],
|
|
272
|
+
isError: true,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
resolvedPersonas.push(persona);
|
|
276
|
+
}
|
|
277
|
+
const sessions = [];
|
|
278
|
+
for (const persona of resolvedPersonas) {
|
|
279
|
+
const session = await walkFlow(url, persona, { maxSteps: max_steps });
|
|
280
|
+
sessions.push(session);
|
|
281
|
+
}
|
|
282
|
+
const comparison = comparePersonaSessions(sessions);
|
|
283
|
+
const report = generateComparisonReport(comparison);
|
|
284
|
+
return { content: [{ type: "text", text: report }] };
|
|
285
|
+
});
|
|
286
|
+
return server;
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|