@usercase/mcp 0.2.11-next.1 → 0.2.11-next.2
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/fix.md +36 -0
- package/mcp/report.js +8 -0
- package/mcp/server.js +98 -1
- package/package.json +1 -1
package/agents/fix.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Fix a problem found by a Usercase research
|
|
2
|
+
|
|
3
|
+
Research: {{research}}
|
|
4
|
+
Finding: {{finding}}
|
|
5
|
+
Report: {{audience}}
|
|
6
|
+
|
|
7
|
+
A Usercase research watched people (or virtual users) do tasks on this site. Its report lists findings — problems on the site, each backed by what people did, what the screen showed in the recordings, and what they wrote. You fix one finding in this repository's code.
|
|
8
|
+
|
|
9
|
+
## 1. Read the finding
|
|
10
|
+
|
|
11
|
+
Call `report_finding` with the research id, the finding key and the report. If no finding key is given above, call `report_findings` first, show the user the list (key, severity, title, and whether someone is already fixing it), and ask which one to fix.
|
|
12
|
+
|
|
13
|
+
Read all of it: the title and why, who it hit, the screen checks (what the page really showed — these come from the recordings, not from opinions), the paths people took, the groups of what they wrote, and the suggested changes. The suggested changes were written without seeing the code; treat them as a direction, not a spec.
|
|
14
|
+
|
|
15
|
+
## 2. Find the code
|
|
16
|
+
|
|
17
|
+
From the pages, buttons and texts in the evidence, find the code behind them: routes, the page components, the button and its click handler, the text shown. Confirm you found the right place by matching the exact text on screen (a button label, a heading) to the code.
|
|
18
|
+
|
|
19
|
+
## 3. Propose the change — before changing anything
|
|
20
|
+
|
|
21
|
+
Tell the user, briefly:
|
|
22
|
+
- what in the code causes the problem, with file paths,
|
|
23
|
+
- the change you propose and why it addresses what people did and said, and
|
|
24
|
+
- anything the finding suggests that you would not do, and why (it would break something, it needs a product decision, the code already does it).
|
|
25
|
+
|
|
26
|
+
Keep the change as small as solves the problem. Do not redesign unrelated parts. Wait for the user's approval or corrections.
|
|
27
|
+
|
|
28
|
+
## 4. Change the code
|
|
29
|
+
|
|
30
|
+
When the user approves, call `report_fix` with `status` `fixing` and the files you will change. Then make the change. Follow the repository's own conventions, and run its checks (type check, lint, tests) if it has them.
|
|
31
|
+
|
|
32
|
+
## 5. Record the fix
|
|
33
|
+
|
|
34
|
+
When the change is done, call `report_fix` with `status` `fixed`, the files changed, a one-sentence summary written for the team (what a visitor now sees differently), and the pull request link if you opened one. Do not commit, push or open a pull request unless the user asks.
|
|
35
|
+
|
|
36
|
+
Then tell the user that the finding shows as fixed in the report, and how to check it worked: {{check}}
|
package/mcp/report.js
CHANGED
|
@@ -292,6 +292,11 @@ const sendSteps = (session, body) => keyed("PUT", `/agent/questions/steps/${enco
|
|
|
292
292
|
/** 리서치 초안(new-research) — 무엇을 리서치할지 고를 근거와, Inbox 초안으로 저장 */
|
|
293
293
|
const getResearchContext = () => keyed("GET", "/agent/researches/context", null, 30000);
|
|
294
294
|
const saveResearchDraft = (body) => keyed("POST", "/agent/researches/drafts", body, 30000);
|
|
295
|
+
/** 리서치 Report(fix) — 발견 목록, 발견 하나와 근거, 고친 기록 */
|
|
296
|
+
const rq = (audience) => (audience === "real" ? "?audience=real" : "?audience=virtual");
|
|
297
|
+
const getReport = (researchId, audience) => keyed("GET", `/agent/researches/${encodeURIComponent(researchId)}/report${rq(audience)}`);
|
|
298
|
+
const getFinding = (researchId, key, audience) => keyed("GET", `/agent/researches/${encodeURIComponent(researchId)}/report/findings/${encodeURIComponent(key)}${rq(audience)}`);
|
|
299
|
+
const saveFix = (researchId, key, body) => keyed("PUT", `/agent/researches/${encodeURIComponent(researchId)}/report/findings/${encodeURIComponent(key)}/fix`, body);
|
|
295
300
|
/** 대시보드 주소. API 주소에서 /koa 를 뗀다 */
|
|
296
301
|
const dashboardUrl = (p) => apiBase().replace(/\/koa$/, "") + p;
|
|
297
302
|
|
|
@@ -315,5 +320,8 @@ module.exports = {
|
|
|
315
320
|
sendSteps,
|
|
316
321
|
getResearchContext,
|
|
317
322
|
saveResearchDraft,
|
|
323
|
+
getReport,
|
|
324
|
+
getFinding,
|
|
325
|
+
saveFix,
|
|
318
326
|
dashboardUrl,
|
|
319
327
|
};
|
package/mcp/server.js
CHANGED
|
@@ -556,6 +556,87 @@ server.registerTool(
|
|
|
556
556
|
|
|
557
557
|
const recorder = new PathRecorder(hub);
|
|
558
558
|
|
|
559
|
+
// ── 리서치 Report 의 발견 고치기(fix) ──
|
|
560
|
+
const audienceArg = z.enum(["virtual", "real"]).optional().describe("Which report: virtual users (default) or real people");
|
|
561
|
+
const fixLine = (f) => (f ? ` — ${f.status === "fixed" ? "fixed" : "being fixed"}${f.agent ? ` by ${f.agent}` : ""}` : "");
|
|
562
|
+
const whoWord = (audience) => (audience === "real" ? "people" : "virtual users");
|
|
563
|
+
|
|
564
|
+
server.registerTool(
|
|
565
|
+
"report_findings",
|
|
566
|
+
{
|
|
567
|
+
description:
|
|
568
|
+
"List the findings of a Usercase research's latest report (key, severity, title, who, and whether one is being fixed). Use when the fix command has no finding key.",
|
|
569
|
+
inputSchema: { research: z.string().describe("The research id"), audience: audienceArg },
|
|
570
|
+
},
|
|
571
|
+
wrap(async ({ research, audience }) => {
|
|
572
|
+
const r = await report.getReport(research, audience);
|
|
573
|
+
if (!r.findings)
|
|
574
|
+
return text(r.making ? "The report is being written right now. Try again in a minute." : `This research has no ${whoWord(r.audience)} report yet. It is written once 5 have finished.`);
|
|
575
|
+
return text(
|
|
576
|
+
[
|
|
577
|
+
`${r.research.title} — report of ${r.people} ${whoWord(r.audience)}`,
|
|
578
|
+
`Summary: ${r.summary}`,
|
|
579
|
+
"",
|
|
580
|
+
...r.findings.map((f) => `${f.key} [${f.severity}] ${f.title} — ${f.who}${fixLine(f.fix)}`),
|
|
581
|
+
"",
|
|
582
|
+
`Report: ${r.url}`,
|
|
583
|
+
].join("\n"),
|
|
584
|
+
);
|
|
585
|
+
}),
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
server.registerTool(
|
|
589
|
+
"report_finding",
|
|
590
|
+
{
|
|
591
|
+
description:
|
|
592
|
+
"Read one finding of a Usercase research report with all its evidence: why, who, the screen checks from the recordings, the paths people took, what they wrote, and the suggested changes. Read it before looking for the code.",
|
|
593
|
+
inputSchema: { research: z.string().describe("The research id"), finding: z.string().describe("Finding key, e.g. F1"), audience: audienceArg },
|
|
594
|
+
},
|
|
595
|
+
wrap(async ({ research, finding, audience }) => {
|
|
596
|
+
const r = await report.getFinding(research, finding, audience);
|
|
597
|
+
const f = r.finding;
|
|
598
|
+
const e = r.evidence;
|
|
599
|
+
const L = [
|
|
600
|
+
`${f.key} [${f.severity}] ${f.title}${fixLine(f.fix)}`,
|
|
601
|
+
`Research: ${r.research.title} (report of ${r.people} ${whoWord(r.audience)})`,
|
|
602
|
+
"",
|
|
603
|
+
`Why: ${f.why}`,
|
|
604
|
+
`Who: ${f.who}`,
|
|
605
|
+
`Where: ${f.where}`,
|
|
606
|
+
];
|
|
607
|
+
if (e.screen.length) L.push("", "What the screen showed (from the recordings):", ...e.screen.map((x) => `- ${x.replace(/^Screen check:\s*/, "")}`));
|
|
608
|
+
if (e.paths.length) L.push("", "Paths people took:", ...e.paths.map((p) => `- ${(p.steps || []).join(" → ")} (${p.people} people)`));
|
|
609
|
+
if (e.words.length) L.push("", "What people wrote (grouped by meaning, with examples):", ...e.words.map((w) => `- ${w.text}`));
|
|
610
|
+
if (e.numbers.length) L.push("", "Numbers:", ...e.numbers.map((x) => `- ${x}`));
|
|
611
|
+
L.push("", "Suggested changes (written without seeing the code):", ...f.changes.map((c, i) => `${i + 1}. ${c}`), `Check after the fix: ${f.measure}`);
|
|
612
|
+
if (r.sessionsUrl) L.push("", `Watch these sessions: ${r.sessionsUrl}`);
|
|
613
|
+
L.push(`Report: ${r.reportUrl}`);
|
|
614
|
+
return text(L.join("\n"));
|
|
615
|
+
}),
|
|
616
|
+
);
|
|
617
|
+
|
|
618
|
+
server.registerTool(
|
|
619
|
+
"report_fix",
|
|
620
|
+
{
|
|
621
|
+
description:
|
|
622
|
+
"Record on the research report that you are fixing a finding (status fixing, once the user approved the change) or have fixed it (status fixed, with the files, a one-sentence summary for the team, and the pull request link if any). The report shows it on that finding.",
|
|
623
|
+
inputSchema: {
|
|
624
|
+
research: z.string().describe("The research id"),
|
|
625
|
+
finding: z.string().describe("Finding key, e.g. F1"),
|
|
626
|
+
audience: audienceArg,
|
|
627
|
+
status: z.enum(["fixing", "fixed"]),
|
|
628
|
+
files: z.array(z.string()).optional().describe("Repository paths of the files changed (or to be changed)"),
|
|
629
|
+
summary: z.string().optional().describe("One sentence for the team: what visitors now see differently"),
|
|
630
|
+
prUrl: z.string().optional().describe("Pull request link, if one was opened"),
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
wrap(async ({ research, finding, audience, status, files, summary, prUrl }) => {
|
|
634
|
+
const agent = CODEX ? "Codex" : OTHER ? null : "Claude Code";
|
|
635
|
+
const r = await report.saveFix(research, finding, { audience: audience || "virtual", status, agent, files, summary, prUrl });
|
|
636
|
+
return text(`${r.key} is marked ${r.status} on the report: ${r.reportUrl}`);
|
|
637
|
+
}),
|
|
638
|
+
);
|
|
639
|
+
|
|
559
640
|
server.registerTool(
|
|
560
641
|
"path_tasks",
|
|
561
642
|
{
|
|
@@ -1047,6 +1128,21 @@ const COMMANDS = {
|
|
|
1047
1128
|
"Only this repository's folder changes. Other repositories keep their own project. Until the new project is approved, the current connection stays.",
|
|
1048
1129
|
].join("\n"),
|
|
1049
1130
|
},
|
|
1131
|
+
// 리서치 Report 의 발견 하나를 고친다 — 대시보드 Report 의 "Fix with your agent" 명령
|
|
1132
|
+
fix: {
|
|
1133
|
+
description: "Fix a finding from a Usercase research report in this repository's code",
|
|
1134
|
+
argsSchema: { research: z.string().optional(), finding: z.string().optional(), audience: z.string().optional() },
|
|
1135
|
+
body: ({ research, finding, audience }) =>
|
|
1136
|
+
agentDoc("fix.md", {
|
|
1137
|
+
research: research || "(ask the user — the dashboard Report shows the command)",
|
|
1138
|
+
finding: finding ? String(finding).toUpperCase() : "(not given — list the findings and ask)",
|
|
1139
|
+
audience: audience === "real" ? "real (the report of real people)" : "virtual (the report of virtual users)",
|
|
1140
|
+
check:
|
|
1141
|
+
audience === "real"
|
|
1142
|
+
? "the next report, made as more people take part, shows whether people still hit it."
|
|
1143
|
+
: `run the same tasks with virtual users again (${cmd("research")} <site> 5 research=${research || "<research id>"}). The next report shows whether they still hit it.`,
|
|
1144
|
+
}),
|
|
1145
|
+
},
|
|
1050
1146
|
steps: {
|
|
1051
1147
|
description: "Write the steps for the question open in your dashboard (New question → Several steps)",
|
|
1052
1148
|
argsSchema: { session: z.string().optional().describe("The window id shown in the dashboard command") },
|
|
@@ -1059,12 +1155,13 @@ if (PLUGIN || CODEX || OTHER) {
|
|
|
1059
1155
|
"instructions",
|
|
1060
1156
|
{
|
|
1061
1157
|
description:
|
|
1062
|
-
"The step-by-step instructions for a Usercase command (research, install, path, question, new-research, steps, connect). The plugin's /usercase:* commands call this; follow what it returns as the user's request.",
|
|
1158
|
+
"The step-by-step instructions for a Usercase command (research, install, path, question, new-research, steps, connect, fix). The plugin's /usercase:* commands call this; follow what it returns as the user's request.",
|
|
1063
1159
|
inputSchema: {
|
|
1064
1160
|
command: z.enum(Object.keys(COMMANDS)),
|
|
1065
1161
|
...COMMANDS.research.argsSchema,
|
|
1066
1162
|
...COMMANDS.path.argsSchema,
|
|
1067
1163
|
...COMMANDS.steps.argsSchema,
|
|
1164
|
+
...COMMANDS.fix.argsSchema,
|
|
1068
1165
|
},
|
|
1069
1166
|
},
|
|
1070
1167
|
wrap(async ({ command, ...args }) => text(COMMANDS[command].body(byName(args, COMMANDS[command].argsSchema)))),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usercase/mcp",
|
|
3
|
-
"version": "0.2.11-next.
|
|
3
|
+
"version": "0.2.11-next.2",
|
|
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",
|