@usercase/mcp 0.2.10-next.2 → 0.2.10-next.4
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 +8 -0
- package/agents/study.md +1 -1
- package/mcp/server.js +40 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,6 +67,14 @@ The number is how many people visit in total (default 5). Above 5, Claude works
|
|
|
67
67
|
|
|
68
68
|
With `unlimited` it keeps going until you press Esc. Every visit uses your Claude usage.
|
|
69
69
|
|
|
70
|
+
### Take part in a research
|
|
71
|
+
|
|
72
|
+
Give a research id from your dashboard and the people take part in it instead of exploring freely — they accept its invitation and do its tasks from the study panel, the way invited participants do. The research must be live.
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
/mcp__usercase__research https://staging.example.com 10 research=<research id>
|
|
76
|
+
```
|
|
77
|
+
|
|
70
78
|
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.
|
|
71
79
|
|
|
72
80
|
### Sites behind a sign-in
|
package/agents/study.md
CHANGED
|
@@ -6,7 +6,7 @@ The user wants the people to **take part in a research** from their Usercase das
|
|
|
6
6
|
- If the status is not `deployed`, stop and tell the user: the site only offers deployed researches, so the visitors could not join it. They can deploy it in the dashboard and run this again.
|
|
7
7
|
- If the Usercase SDK is not running on the site, stop — without it there is no research to join.
|
|
8
8
|
|
|
9
|
-
Show the user the tasks in one short list before casting, so they know what the people will be asked to do.
|
|
9
|
+
Show the user the tasks in one short list before casting, so they know what the people will be asked to do — as information, not as a question. Then go straight on.
|
|
10
10
|
|
|
11
11
|
**The research decides what the visit is about — do not ask the user about it.** Not what the visitors should try, how far they should go, which tasks to cover or what to focus on: the participants get the research's tasks from the study panel and do what they would do. The only questions for the user are the ones the steps below always ask — demo accounts (if parts of the tasks need sign-in) and whether writes are sent — asked as those questions, in those words, and not merged with anything about the research. A task that needs a sign-in the visitors do not have is still run: what happens to a participant without an account is a result.
|
|
12
12
|
|
package/mcp/server.js
CHANGED
|
@@ -562,9 +562,20 @@ server.registerTool(
|
|
|
562
562
|
description: "List the tasks of a Usercase research (from the dashboard) and whether each already has an expected path. Call first in the path procedure.",
|
|
563
563
|
inputSchema: { research: z.string().describe("The research id, as shown in the dashboard command") },
|
|
564
564
|
},
|
|
565
|
-
wrap(async ({ research }) =>
|
|
565
|
+
wrap(async ({ research }) => {
|
|
566
|
+
const out = await recorder.tasks(research);
|
|
567
|
+
// 리서치에 참여시키는 판(research=<id>)에서 그 리서치를 읽은 것이면 — 과제를 읽은 바로 이 자리에서 못 박는다.
|
|
568
|
+
// 지시문 중간의 규칙만으로는 "어느 과제까지 시킬까"를 사용자에게 물었다(10/30 라이브 bluetags, 로그인이 필요한 Follow 과제)
|
|
569
|
+
return text(run && run.research && run.research === String(research).trim() ? `${out}\n\n${STUDY_TASKS_NOTE}` : out);
|
|
570
|
+
}),
|
|
566
571
|
);
|
|
567
572
|
|
|
573
|
+
const STUDY_TASKS_NOTE = [
|
|
574
|
+
"These are the tasks every participant in this run does — all of them, in order, from the study panel, whatever they ask (including tasks that need signing in or sending something). That is fixed by the research and is not the user's choice in this run.",
|
|
575
|
+
"Do not ask the user which tasks or actions the visitors should do, how far they should go, or what to focus on. Show these tasks to the user as a plain list for their information, then go straight on to casting.",
|
|
576
|
+
"The only questions for the user are the demo-account and writes questions the instructions give, each asked as its own question in its own words.",
|
|
577
|
+
].join("\n");
|
|
578
|
+
|
|
568
579
|
server.registerTool(
|
|
569
580
|
"path_start",
|
|
570
581
|
{
|
|
@@ -917,6 +928,30 @@ const pathWalkRules = () => {
|
|
|
917
928
|
.trim();
|
|
918
929
|
};
|
|
919
930
|
|
|
931
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* 명령 인자를 이름으로 맞춘다. Claude Code 는 /mcp__usercase__research 의 인자를 띄어쓰기로 나눠 순서대로 넘긴다 —
|
|
935
|
+
* `research=<id>` 라고 써도 세 번째 자리(focus)에 "research=<id>" 로 들어가, 리서치가 아니라 초점 판으로 돌았다(10/30 라이브 bluetags).
|
|
936
|
+
* 그래서 어느 자리에 있든 `이름=값`(또는 `이름:값`)이면 그 이름으로 옮기고, research 를 받는 명령에서는 이름 없는 리서치 id(UUID)도 research 로 본다
|
|
937
|
+
*/
|
|
938
|
+
function byName(args = {}, schema = {}) {
|
|
939
|
+
const names = Object.keys(schema || {});
|
|
940
|
+
const out = {};
|
|
941
|
+
const loose = [];
|
|
942
|
+
for (const [k, raw] of Object.entries(args || {})) {
|
|
943
|
+
if (raw == null) continue;
|
|
944
|
+
const v = String(raw).trim();
|
|
945
|
+
const m = v.match(/^([a-z]+)\s*[=:]\s*(.*)$/is);
|
|
946
|
+
if (m && names.includes(m[1].toLowerCase())) out[m[1].toLowerCase()] = m[2].trim();
|
|
947
|
+
else if (names.includes("research") && UUID.test(v.replace(/^research\s+/i, ""))) out.research = v.replace(/^research\s+/i, "");
|
|
948
|
+
else loose.push([k, v]);
|
|
949
|
+
}
|
|
950
|
+
// 이름 없이 온 값은 제자리에 — 이름으로 이미 채운 자리는 덮지 않는다
|
|
951
|
+
for (const [k, v] of loose) if (!(k in out) && v !== "") out[k] = v;
|
|
952
|
+
return out;
|
|
953
|
+
}
|
|
954
|
+
|
|
920
955
|
/**
|
|
921
956
|
* 명령 지시문. 따로 쓰면 MCP 프롬프트(/mcp__usercase__research), 플러그인이면 instructions 도구(/usercase:research 스킬이 부른다)로
|
|
922
957
|
* 같은 글이 나간다
|
|
@@ -935,7 +970,8 @@ const COMMANDS = {
|
|
|
935
970
|
},
|
|
936
971
|
body: ({ url, count, focus, research }) => {
|
|
937
972
|
const plan = researchPlan(count);
|
|
938
|
-
|
|
973
|
+
// research — 이 판이 참여하는 리서치. path_tasks 가 그 리서치를 읽을 때 과제를 고르지 말라고 덧붙인다
|
|
974
|
+
run = { total: plan.total, since: Date.now(), research: (research || "").trim() || null };
|
|
939
975
|
// 사람 만드는 규칙은 우리 AI 로 만들 때와 같은 원본(people.md)에서 가져온다 — 두 벌로 두지 않는다.
|
|
940
976
|
// 자유 탐방이라 원본이 말하는 "path" 는 "자기 목적으로 왔다" 뿐이다
|
|
941
977
|
const rules =
|
|
@@ -1031,12 +1067,12 @@ if (PLUGIN || CODEX || OTHER) {
|
|
|
1031
1067
|
...COMMANDS.steps.argsSchema,
|
|
1032
1068
|
},
|
|
1033
1069
|
},
|
|
1034
|
-
wrap(async ({ command, ...args }) => text(COMMANDS[command].body(args))),
|
|
1070
|
+
wrap(async ({ command, ...args }) => text(COMMANDS[command].body(byName(args, COMMANDS[command].argsSchema)))),
|
|
1035
1071
|
);
|
|
1036
1072
|
} else {
|
|
1037
1073
|
for (const [name, c] of Object.entries(COMMANDS)) {
|
|
1038
1074
|
const meta = c.argsSchema ? { description: c.description, argsSchema: c.argsSchema } : { description: c.description };
|
|
1039
|
-
server.registerPrompt(name, meta, (args = {}) => ({ messages: [{ role: "user", content: { type: "text", text: c.body(args) } }] }));
|
|
1075
|
+
server.registerPrompt(name, meta, (args = {}) => ({ messages: [{ role: "user", content: { type: "text", text: c.body(byName(args, c.argsSchema)) } }] }));
|
|
1040
1076
|
}
|
|
1041
1077
|
}
|
|
1042
1078
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usercase/mcp",
|
|
3
|
-
"version": "0.2.10-next.
|
|
3
|
+
"version": "0.2.10-next.4",
|
|
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",
|