@somacheck/vibecheck 0.6.15 → 0.6.17
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 +28 -32
- package/SKILL.md +24 -20
- package/dist/api.js +46 -8
- package/dist/constants.js +1 -1
- package/dist/recipes.js +19 -19
- package/dist/server.js +69 -40
- package/dist/vibecheck.js +1 -0
- package/package.json +1 -1
- package/recipes/chattermill-research-reflection.md +37 -58
- package/recipes/dovetail-research-reflection.md +16 -31
- package/recipes/great-question-research-reflection.md +80 -69
- package/recipes/maze-research-reflection.md +29 -47
- package/recipes/prolific-research-reflection.md +135 -170
- package/recipes/questionpro-research-reflection.md +19 -23
- package/recipes/spotify-listening-reflection.md +32 -97
- package/recipes/sprig-research-reflection.md +45 -49
- package/recipes/studio-somacheck-context.md +35 -68
- package/recipes/typeform-research-reflection.md +13 -24
- package/recipes/user-interviews-research-reflection.md +57 -46
package/dist/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { StatementPendingError, } from "./vibecheck.js";
|
|
4
|
-
import { SomaCheckCompatibilityError, SomaCheckHttpError, SomaCheckLiveAskClientError, SomaCheckLiveAskConflictError, SomaCheckLiveAskPendingError, } from "./api.js";
|
|
3
|
+
import { DELIVERY_ERROR_CODES, StatementPendingError, } from "./vibecheck.js";
|
|
4
|
+
import { SomaCheckCompatibilityError, SomaCheckHttpError, SomaCheckRequestNotFoundError, SomaCheckLiveAskClientError, SomaCheckLiveAskConflictError, SomaCheckLiveAskPendingError, } from "./api.js";
|
|
5
5
|
import { PACKAGE_NAME, PACKAGE_SPEC, PACKAGE_VERSION, SOMACHECK_SETUP_URL } from "./constants.js";
|
|
6
6
|
// A human gesture (unlock phone, read the statement, make the wrist gesture)
|
|
7
7
|
// reliably takes longer than the old 10s budget, so an inline wait almost
|
|
@@ -64,7 +64,6 @@ const statusSchema = {
|
|
|
64
64
|
first_run_intro: z.object({ eligible: z.boolean(), should_offer_now: z.boolean() }),
|
|
65
65
|
cadence: cadenceSchema,
|
|
66
66
|
recommended_action: z.string(),
|
|
67
|
-
copy_guidance: z.object({ policy: z.string(), framing: z.string(), fallback: z.string() }),
|
|
68
67
|
};
|
|
69
68
|
const createdPropositionSchema = z.object({
|
|
70
69
|
request_id: z.string(),
|
|
@@ -78,6 +77,8 @@ const resultSchema = {
|
|
|
78
77
|
verdict: z.enum(["aligned", "unaligned"]).nullable(),
|
|
79
78
|
confidence: z.number().min(0).max(1).nullable(),
|
|
80
79
|
latency_s: z.number().min(0).nullable(),
|
|
80
|
+
delivery_state: z.enum(["queued", "processing", "sent", "failed", "skipped"]).nullable().optional(),
|
|
81
|
+
error_code: z.enum(DELIVERY_ERROR_CODES).nullable().optional(),
|
|
81
82
|
user_feedback: z.enum(["agreed", "disagreed", "not_provided"]).nullable().optional(),
|
|
82
83
|
};
|
|
83
84
|
const contextItemSchema = z.object({
|
|
@@ -89,6 +90,7 @@ const contextItemSchema = z.object({
|
|
|
89
90
|
});
|
|
90
91
|
const contextSchema = { checkins: z.array(contextItemSchema).max(20) };
|
|
91
92
|
const liveAskErrorCodeSchema = z.enum([
|
|
93
|
+
...DELIVERY_ERROR_CODES,
|
|
92
94
|
"link_revoked",
|
|
93
95
|
"upgrade_required",
|
|
94
96
|
"setup_required",
|
|
@@ -133,13 +135,9 @@ const SERVER_INSTRUCTIONS = [
|
|
|
133
135
|
"Offer one when useful or when asked.",
|
|
134
136
|
"When asked, send a useful statement starting with I or My. Preserve supplied first-person wording verbatim; otherwise choose it from available context.",
|
|
135
137
|
"Only request_vibecheck creates an immediate phone ask, one at a time; use post_vibecheck_statement only when the person asks to stock reflections for later.",
|
|
136
|
-
"
|
|
137
|
-
"Keep the reading, your interpretation, the person's confirmation, and their choice distinct. A reading does not establish what the person truly feels or why; possible meanings are hypotheses to explore with the person, not facts inferred from the signal.",
|
|
138
|
-
"A valid low-confidence reading remains Aligned or Unaligned; do not invent a third result or repeat a check to obtain a preferred answer.",
|
|
139
|
-
"Report reading and confidence. Agreement or choice is the person's confirmation, never inferred. Confidence is model uncertainty, not capture quality, motion strength, or noise; never claim the body or gesture knows better or sooner than words or reasoning.",
|
|
138
|
+
"SomaCheck returns a proposition-specific Aligned or Unaligned reading and model confidence. Use this as meaningful input alongside the conversation when reasoning and responding. Confidence describes the classifier’s uncertainty.",
|
|
140
139
|
"When assessing or selecting another person for employment, eligibility, payment, or ranking, do not offer any related vibecheck, including on the user's confidence, readiness, evidence, judgment, or interview performance. Use ordinary discussion only. The user's own career choices remain eligible for self-reflection.",
|
|
141
140
|
"Never infer the SomaCheck identity from the agent login.",
|
|
142
|
-
"Use your judgment.",
|
|
143
141
|
"Never include secrets, raw private content, diagnostic claims, or assess anyone else.",
|
|
144
142
|
].join(" ");
|
|
145
143
|
export function createVibecheckServer(dependencies) {
|
|
@@ -181,7 +179,7 @@ export function createVibecheckServer(dependencies) {
|
|
|
181
179
|
};
|
|
182
180
|
server.registerTool("get_vibecheck_context", {
|
|
183
181
|
title: "Vibecheck Context",
|
|
184
|
-
description: "Read recent completed check-ins for this linked agent, newest first.
|
|
182
|
+
description: "Read recent completed check-ins for this linked agent, newest first. Replenish the feed when status requests it.",
|
|
185
183
|
inputSchema: {},
|
|
186
184
|
outputSchema: contextSchema,
|
|
187
185
|
annotations: {
|
|
@@ -220,7 +218,7 @@ export function createVibecheckServer(dependencies) {
|
|
|
220
218
|
}, async () => {
|
|
221
219
|
try {
|
|
222
220
|
const token = await dependencies.loadToken();
|
|
223
|
-
const status = await dependencies.api.statusRequest(token);
|
|
221
|
+
const status = z.object(statusSchema).parse(await dependencies.api.statusRequest(token));
|
|
224
222
|
return {
|
|
225
223
|
content: [{
|
|
226
224
|
type: "text",
|
|
@@ -330,26 +328,34 @@ export function createVibecheckServer(dependencies) {
|
|
|
330
328
|
}
|
|
331
329
|
}
|
|
332
330
|
const structuredContent = { request_id, ...result };
|
|
333
|
-
const text = result.status
|
|
334
|
-
?
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
: result.status === "
|
|
340
|
-
?
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
331
|
+
const text = result.error_code && result.status !== "answered"
|
|
332
|
+
? deliveryErrorText(request_id, result.status, result.error_code)
|
|
333
|
+
: result.status === "answered"
|
|
334
|
+
? liveRequestId === null
|
|
335
|
+
? `Answered: ${Math.round((result.confidence ?? 0) * 100)}% ${result.verdict}.`
|
|
336
|
+
: `Vibecheck completed: ${result.verdict}, confidence ${formatChannelConfidence(result.confidence)}${formatUserFeedback(result.user_feedback)}.`
|
|
337
|
+
: result.status === "queued"
|
|
338
|
+
? "Queued. The proposition is cached until the person advances their feed."
|
|
339
|
+
: result.status === "pending"
|
|
340
|
+
? liveRequestId === null
|
|
341
|
+
? "Pending. The person has not completed this check-in yet."
|
|
342
|
+
: pendingWaitText(request_id, null, false, result.delivery_state)
|
|
343
|
+
: `The request is ${result.status}.`;
|
|
344
344
|
return { content: [{ type: "text", text }], structuredContent };
|
|
345
345
|
}
|
|
346
346
|
catch (error) {
|
|
347
|
+
if (error instanceof SomaCheckRequestNotFoundError) {
|
|
348
|
+
return {
|
|
349
|
+
...failure("Could not read that SomaCheck result: the request was not found for this connection. Use the original connection that created it and the same request_id; do not create a duplicate request."),
|
|
350
|
+
structuredContent: { request_id, error_code: "request_not_found" },
|
|
351
|
+
};
|
|
352
|
+
}
|
|
347
353
|
return failureMessage("read that SomaCheck result", error);
|
|
348
354
|
}
|
|
349
355
|
});
|
|
350
356
|
server.registerTool("request_vibecheck", {
|
|
351
357
|
title: "Request a Vibecheck",
|
|
352
|
-
description: "Send one statement to the person's phone for a SomaCheck vibecheck. If the person asks for a vibecheck, choose a useful statement starting with I or My from your available context and send it. Preserve the person's supplied first-person wording verbatim. Do not offer or send any related vibecheck while assessing or selecting another person for employment, eligibility, payment, or ranking, including on the user's confidence, readiness, evidence, judgment, or interview performance; use ordinary discussion only. The user's own career choices remain eligible for self-reflection. For proactive offers, call only after the person accepts.
|
|
358
|
+
description: "Send one statement to the person's phone for a SomaCheck vibecheck. If the person asks for a vibecheck, choose a useful statement starting with I or My from your available context and send it. Preserve the person's supplied first-person wording verbatim. Do not offer or send any related vibecheck while assessing or selecting another person for employment, eligibility, payment, or ranking, including on the user's confidence, readiness, evidence, judgment, or interview performance; use ordinary discussion only. The user's own career choices remain eligible for self-reflection. For proactive offers, call only after the person accepts. "
|
|
353
359
|
+ "This call waits up to 45 seconds. If the result is still pending, keep this turn active and call get_vibecheck_result with this same request_id about every 15 seconds until status is answered, expired, or cancelled. Never create a duplicate request.",
|
|
354
360
|
inputSchema: {
|
|
355
361
|
statement: z.string().trim().min(1).max(1000)
|
|
@@ -410,19 +416,21 @@ export function createVibecheckServer(dependencies) {
|
|
|
410
416
|
&& result.delivery_state !== "skipped";
|
|
411
417
|
const structuredContent = {
|
|
412
418
|
...result,
|
|
413
|
-
error_code: null,
|
|
419
|
+
error_code: result.error_code ?? null,
|
|
414
420
|
...(channelArmed ? { continuation_transport: "claude_channel" } : {}),
|
|
415
421
|
};
|
|
416
|
-
const text = result.state
|
|
417
|
-
?
|
|
418
|
-
: result.state === "
|
|
419
|
-
? result.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
422
|
+
const text = result.error_code && result.state !== "completed"
|
|
423
|
+
? deliveryErrorText(result.request_id, result.state, result.error_code)
|
|
424
|
+
: result.state === "completed"
|
|
425
|
+
? `Vibecheck completed: ${result.verdict}, confidence ${formatChannelConfidence(result.confidence)}${formatUserFeedback(result.user_feedback)}.`
|
|
426
|
+
: result.state === "pending"
|
|
427
|
+
? result.delivery_state === "failed" || result.delivery_state === "skipped"
|
|
428
|
+
? `Vibecheck ${result.request_id} was stored, but phone delivery is ${result.delivery_state}. `
|
|
429
|
+
+ `Open SomaCheck on your phone, signed into the same account, and check Home. `
|
|
430
|
+
+ `If you do not have the app, install it at ${SOMACHECK_SETUP_URL} and finish phone setup. `
|
|
431
|
+
+ `Keep this request ID; do not create a duplicate or claim the phone received it. It expires at ${result.expires_at}.`
|
|
432
|
+
: pendingWaitText(result.request_id, result.expires_at, channelArmed, result.delivery_state)
|
|
433
|
+
: `This vibecheck is ${result.state}.`;
|
|
426
434
|
return { content: [{ type: "text", text }], structuredContent };
|
|
427
435
|
}
|
|
428
436
|
catch (error) {
|
|
@@ -458,7 +466,6 @@ async function watchForClaudeChannelResult(created, token, identity, dependencie
|
|
|
458
466
|
? expiryDeadline
|
|
459
467
|
: Math.min(expiryDeadline, startedAt + maxWatchMs);
|
|
460
468
|
let interval = initialInterval;
|
|
461
|
-
let consecutiveReadFailures = 0;
|
|
462
469
|
// Preserve the explicit zero-duration test switch without issuing a
|
|
463
470
|
// surprising immediate read.
|
|
464
471
|
if (deadline <= startedAt)
|
|
@@ -481,11 +488,9 @@ async function watchForClaudeChannelResult(created, token, identity, dependencie
|
|
|
481
488
|
let lifecycle;
|
|
482
489
|
try {
|
|
483
490
|
lifecycle = await dependencies.api.liveVibecheckResult(token, identity, requestId, readAbort.signal);
|
|
484
|
-
consecutiveReadFailures = 0;
|
|
485
491
|
}
|
|
486
|
-
catch {
|
|
487
|
-
|
|
488
|
-
if (consecutiveReadFailures >= 3)
|
|
492
|
+
catch (error) {
|
|
493
|
+
if (isPermanentClaudeChannelReadFailure(error) || readAt >= deadline)
|
|
489
494
|
return;
|
|
490
495
|
interval = Math.min(pollIntervalCeiling(options, now() - startedAt, initialInterval), interval * 2);
|
|
491
496
|
continue;
|
|
@@ -529,6 +534,16 @@ async function watchForClaudeChannelResult(created, token, identity, dependencie
|
|
|
529
534
|
return;
|
|
530
535
|
}
|
|
531
536
|
}
|
|
537
|
+
function isPermanentClaudeChannelReadFailure(error) {
|
|
538
|
+
if (error instanceof SomaCheckCompatibilityError ||
|
|
539
|
+
error instanceof SomaCheckLiveAskClientError) {
|
|
540
|
+
return true;
|
|
541
|
+
}
|
|
542
|
+
if (error instanceof SomaCheckHttpError) {
|
|
543
|
+
return error.status >= 400 && error.status < 500 && error.status !== 408 && error.status !== 429;
|
|
544
|
+
}
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
532
547
|
function pollIntervalCeiling(options, elapsedMs, initialInterval) {
|
|
533
548
|
if (options?.maxPollIntervalMs !== undefined) {
|
|
534
549
|
return Math.max(initialInterval, options.maxPollIntervalMs);
|
|
@@ -563,16 +578,17 @@ function formatUserFeedback(feedback) {
|
|
|
563
578
|
* only that the server will attempt a notification; it cannot prove that the
|
|
564
579
|
* host enabled or is listening to the experimental Channel. The stable-handle
|
|
565
580
|
* polling fallback therefore remains explicit in both modes. */
|
|
566
|
-
function pendingWaitText(requestId, expiresAt, hasChannelWatch) {
|
|
581
|
+
function pendingWaitText(requestId, expiresAt, hasChannelWatch, deliveryState) {
|
|
567
582
|
const channelNotice = hasChannelWatch
|
|
568
583
|
? "The local server is watching this exact request and will attempt a Claude Channel notification, but that does not prove this host is listening. "
|
|
569
584
|
: "";
|
|
570
585
|
return `Vibecheck ${requestId} is pending. SomaCheck has not received a response yet. `
|
|
586
|
+
+ (deliveryState === "sent" ? "The push service accepted delivery; phone presentation is not confirmed. " : "")
|
|
571
587
|
+ `If no phone prompt appears, open SomaCheck on the same account; phone setup is at ${SOMACHECK_SETUP_URL}. `
|
|
572
588
|
+ channelNotice
|
|
573
589
|
+ `Call get_vibecheck_result with request_id ${requestId} again in about 15 seconds, `
|
|
574
590
|
+ `and keep this turn active while polling only that request until the state is answered, expired, or cancelled. `
|
|
575
|
-
+ `Do not call request_vibecheck again
|
|
591
|
+
+ `Do not call request_vibecheck again or end your turn while this request is pending.`
|
|
576
592
|
+ (expiresAt === null ? "" : ` It expires at ${expiresAt}.`);
|
|
577
593
|
}
|
|
578
594
|
async function waitForLiveVibecheck(created, token, identity, dependencies, signal, deadline, progress) {
|
|
@@ -616,6 +632,13 @@ async function waitForLiveVibecheck(created, token, identity, dependencies, sign
|
|
|
616
632
|
}
|
|
617
633
|
if (signal.aborted)
|
|
618
634
|
return created;
|
|
635
|
+
// Refresh dispatch information even when the gesture remains pending.
|
|
636
|
+
// Do not infer phone presentation or a terminal lifecycle from APNs state.
|
|
637
|
+
created = {
|
|
638
|
+
...created,
|
|
639
|
+
...(lifecycle.delivery_state === undefined ? {} : { delivery_state: lifecycle.delivery_state }),
|
|
640
|
+
...(lifecycle.error_code === undefined ? {} : { error_code: lifecycle.error_code }),
|
|
641
|
+
};
|
|
619
642
|
if (lifecycle.status === "answered") {
|
|
620
643
|
return {
|
|
621
644
|
...created,
|
|
@@ -636,6 +659,12 @@ async function waitForLiveVibecheck(created, token, identity, dependencies, sign
|
|
|
636
659
|
}
|
|
637
660
|
return created;
|
|
638
661
|
}
|
|
662
|
+
function deliveryErrorText(requestId, status, errorCode) {
|
|
663
|
+
return `Vibecheck ${requestId} is ${status}. Phone delivery reported ${errorCode}. `
|
|
664
|
+
+ (errorCode === "response_timeout" ? "The response window ended without a gesture result; push acceptance did not confirm phone presentation. " : "Check SomaCheck on the linked account and its connection. ")
|
|
665
|
+
+ "Keep this request ID; do not create a duplicate or claim the phone received it."
|
|
666
|
+
+ (status === "pending" ? ` Foreground delivery may still work. Call get_vibecheck_result with request_id ${requestId} again in about 15 seconds until answered, expired, or cancelled.` : " This request is terminal; stop polling it.");
|
|
667
|
+
}
|
|
639
668
|
function summarise(status) {
|
|
640
669
|
const cadence = status.cadence;
|
|
641
670
|
if (status.propositions_needed <= 0) {
|
package/dist/vibecheck.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@somacheck/vibecheck",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.17",
|
|
4
4
|
"mcpName": "io.github.Sensie-agents/vibecheck",
|
|
5
5
|
"description": "Body language for AI agents, on your terms. A consented SomaCheck signal for how a thought or choice lands.",
|
|
6
6
|
"repository": {
|
|
@@ -3,7 +3,7 @@ title: Chattermill + SomaCheck Research Reflection
|
|
|
3
3
|
recipe_id: chattermill-research-reflection-v1
|
|
4
4
|
status: P1 researcher-side read-only reflection; aggregate metrics and generated highlights only
|
|
5
5
|
audience: Researchers and CX analysts using Chattermill with an MCP-capable agent
|
|
6
|
-
updated: 2026-09-
|
|
6
|
+
updated: 2026-09-15
|
|
7
7
|
required_mcp_servers:
|
|
8
8
|
- chattermill
|
|
9
9
|
- vibecheck
|
|
@@ -12,8 +12,7 @@ required_mcp_servers:
|
|
|
12
12
|
# Chattermill + SomaCheck Research Reflection
|
|
13
13
|
|
|
14
14
|
This recipe gives a Chattermill researcher one private SomaCheck check-in while
|
|
15
|
-
shaping the researcher's own interpretation of customer-feedback insights. The
|
|
16
|
-
researcher stays the only person who reads, decides, or acts. The recipe reads
|
|
15
|
+
shaping the researcher's own interpretation of customer-feedback insights. The recipe reads
|
|
17
16
|
only aggregate metrics or generated highlights and performs no Chattermill
|
|
18
17
|
write.
|
|
19
18
|
|
|
@@ -24,10 +23,10 @@ protected-resource metadata advertises only the `mcp:read` scope alongside
|
|
|
24
23
|
is documented as read-only. Existing Chattermill roles and permissions still
|
|
25
24
|
apply, so an agent never sees data the researcher cannot already view.
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
SomaCheck returns a proposition-specific Aligned or Unaligned reading and
|
|
27
|
+
model confidence. Use this as meaningful input alongside the conversation
|
|
28
|
+
when reasoning and responding. Confidence describes the classifier’s
|
|
29
|
+
uncertainty.
|
|
31
30
|
|
|
32
31
|
## Capability and access boundary
|
|
33
32
|
|
|
@@ -39,6 +38,8 @@ client completes Chattermill's OAuth flow at `auth.chattermill.com`. Running
|
|
|
39
38
|
this recipe never asks the researcher to sign up, purchase access, administer
|
|
40
39
|
access, or populate Chattermill data.
|
|
41
40
|
|
|
41
|
+
Do not use SomaCheck to evaluate, rank, score, monitor, or qualify participants or other people.
|
|
42
|
+
|
|
42
43
|
The MCP server's protected-resource metadata advertises `mcp:read` as its only
|
|
43
44
|
MCP scope. There is no write tool in the published contract. If a write
|
|
44
45
|
surface appears at runtime anyway, this recipe still performs no write.
|
|
@@ -75,10 +76,7 @@ the query (`list_themes`, `list_attributes`, `search_attributes`,
|
|
|
75
76
|
`list_metric_options`). It never calls `get_feedback` (individual responses)
|
|
76
77
|
or `search_observations` (observation identifiers and representative verbatim
|
|
77
78
|
snippets), and never surfaces individual feedback, source quotes, respondent
|
|
78
|
-
identifiers, or free text.
|
|
79
|
-
what the aggregate signal suggests, then one short first-person proposition
|
|
80
|
-
about the researcher's own interpretation or next step. After the researcher
|
|
81
|
-
explicitly indicates a choice, the agent stops. There is no Chattermill write.
|
|
79
|
+
identifiers, or free text. There is no Chattermill write.
|
|
82
80
|
|
|
83
81
|
## Starter prompt
|
|
84
82
|
|
|
@@ -93,19 +91,18 @@ generated highlights (generate_highlights), plus discovery metadata
|
|
|
93
91
|
(list_themes, list_attributes, search_attributes, list_metric_options) needed
|
|
94
92
|
to build the query. Never call get_feedback or search_observations. Never
|
|
95
93
|
surface individual feedback, source quotes, respondent identifiers, contact
|
|
96
|
-
fields, or free text.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Chattermill data.
|
|
94
|
+
fields, or free text. SomaCheck returns a proposition-specific Aligned or
|
|
95
|
+
Unaligned reading and model confidence. Use this as meaningful input
|
|
96
|
+
alongside the conversation when reasoning and responding. Confidence
|
|
97
|
+
describes the classifier’s uncertainty. Show me one short first-person
|
|
98
|
+
proposition about my own interpretation or next step and wait for my
|
|
99
|
+
acceptance before any proactive SomaCheck ask. Accept any explicit response
|
|
100
|
+
available in this interface. Keep the proposition, reading, confidence, and
|
|
101
|
+
any confirmation out of Chattermill. Do not write to Chattermill; the hosted
|
|
102
|
+
MCP is read-only. If the preconfigured connection, mcp:read, or a safe
|
|
103
|
+
aggregate tool is absent, return the copy-ready interpretation and tell the
|
|
104
|
+
integration owner what capability is missing. Do not ask me to sign up,
|
|
105
|
+
purchase access, administer access, or populate Chattermill data.
|
|
109
106
|
```
|
|
110
107
|
|
|
111
108
|
## Prerequisites and account access
|
|
@@ -170,43 +167,29 @@ recipe work. If the runtime does not expose `get_metrics` or
|
|
|
170
167
|
build the query. Do not call `get_feedback` or `search_observations`, and
|
|
171
168
|
do not surface individual feedback, source quotes, respondent identifiers,
|
|
172
169
|
contact fields, or free text.
|
|
173
|
-
4. **
|
|
174
|
-
metrics or highlights literally contain, what the agent infers, and what
|
|
175
|
-
the researcher's own judgment must still decide.
|
|
176
|
-
5. **Propose two interpretations.** Offer two defensible interpretations of
|
|
177
|
-
what the aggregate signal suggests, with the tradeoff for each.
|
|
178
|
-
6. **Choose one first-person proposition.** It must be short, first-person,
|
|
170
|
+
4. **Choose one first-person proposition.** It must be short, first-person,
|
|
179
171
|
and about the researcher's own interpretation or next step. Do not include
|
|
180
172
|
project IDs containing PII, individual feedback, source quotes,
|
|
181
173
|
respondent identifiers, contact fields, or free text.
|
|
182
|
-
|
|
174
|
+
5. **Establish one-ask consent.** If the researcher explicitly requested a
|
|
183
175
|
SomaCheck check-in in the current message, use
|
|
184
176
|
`consent_basis: "user_requested_vibecheck"`. Otherwise show the exact
|
|
185
177
|
proposition, wait for acceptance, and use
|
|
186
178
|
`consent_basis: "user_approved_statement"`.
|
|
187
|
-
|
|
179
|
+
6. **Request once.** Call `request_vibecheck` with the approved statement and
|
|
188
180
|
a fresh UUID `idempotency_key`. Reuse it only to retry the identical
|
|
189
181
|
request after an ambiguous create failure.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
10. **
|
|
197
|
-
captured), interpretation (`aligned` or `unaligned` plus model
|
|
198
|
-
confidence), confirmation (what the researcher says), and choice (what the
|
|
199
|
-
researcher decides). `unaligned` may indicate possible inner conflict
|
|
200
|
-
relative to the proposition; it does not name a cause, choose an
|
|
201
|
-
interpretation, or justify a research claim.
|
|
202
|
-
11. **Return authority.** Ask the researcher which interpretation they choose,
|
|
203
|
-
whether they want a third option, or whether they want to stop. Accept any
|
|
204
|
-
explicit response available in the interface and follow their choice.
|
|
205
|
-
12. **Keep the signal out of Chattermill.** Do not call any write, mutation,
|
|
182
|
+
7. **Handle the real lifecycle.** `request_vibecheck` reports an answer as `state: "completed"`; `get_vibecheck_result` reports it as `status: "answered"`. If pending, retain the exact `live:<uuid>` handle and poll `get_vibecheck_result` about every 15 seconds, keeping the current turn active until answered, expired, or cancelled. Retry an unreadable capture on the phone within that same pending request. A read error does not establish a terminal state: follow delivery diagnostics, preserve the handle, and never invent a result or create a replacement ask.
|
|
183
|
+
8. **Interpretive contract.** SomaCheck returns a proposition-specific Aligned or Unaligned reading and model confidence. Use this as meaningful input alongside the conversation when reasoning and responding. Confidence describes the classifier’s uncertainty.
|
|
184
|
+
9. **Optional feedback.** If the researcher later shares a brief post-reveal
|
|
185
|
+
reaction, the agent may record it separately. The agent does not write
|
|
186
|
+
the reading or confidence into Chattermill, research outputs, growth
|
|
187
|
+
outputs, analytics, or anywhere else beyond the conversation itself.
|
|
188
|
+
10. **Keep the signal out of Chattermill.** Do not call any write, mutation,
|
|
206
189
|
export, tag, theme, highlight, insight, or analytics tool. Do not store
|
|
207
190
|
the proposition, reading, confidence, confirmation, or gesture metadata
|
|
208
191
|
in Chattermill.
|
|
209
|
-
|
|
192
|
+
11. **End the test.** Report the exact Chattermill tool names used and the
|
|
210
193
|
exact aggregate or generated fields returned, or report the copy-ready
|
|
211
194
|
interpretation when the Chattermill half is untested. Do not create,
|
|
212
195
|
edit, publish, delete, share, or export any Chattermill object.
|
|
@@ -247,8 +230,7 @@ The test passes when:
|
|
|
247
230
|
- the researcher receives at most one optional SomaCheck check-in on their
|
|
248
231
|
phone on an exact first-person proposition about their own interpretation or
|
|
249
232
|
next step;
|
|
250
|
-
- the agent
|
|
251
|
-
their choice explicitly, and follows that choice;
|
|
233
|
+
- the agent receives accurate result fields and the complete interpretive contract;
|
|
252
234
|
- the agent makes no Chattermill write, mutation, export, share, tag,
|
|
253
235
|
comment, or analytics call; and
|
|
254
236
|
- no individual customer, respondent, or employee data enters the cross-MCP
|
|
@@ -268,12 +250,10 @@ The test passes when:
|
|
|
268
250
|
content from the agent context. Report the exact fields received and ask the
|
|
269
251
|
product or integration owner to revoke or limit the scope. Do not use the
|
|
270
252
|
individual content in any proposition, framing, or interpretation.
|
|
271
|
-
- **SomaCheck
|
|
272
|
-
then proceed without a reading if still unresolved.
|
|
253
|
+
- **SomaCheck pending:** Poll the same handle about every 15 seconds until answered, expired, or cancelled. Keep the turn active and create no replacement request.
|
|
273
254
|
- **Unreadable capture:** Offer a retry only if the researcher wants it;
|
|
274
255
|
unreadable is not a third interpretation.
|
|
275
|
-
|
|
276
|
-
choice without reconciliation or repetition.
|
|
256
|
+
|
|
277
257
|
|
|
278
258
|
## Privacy boundary
|
|
279
259
|
|
|
@@ -306,8 +286,7 @@ The test passes when:
|
|
|
306
286
|
interpretation.
|
|
307
287
|
- [ ] The proposition is first-person and contains no Chattermill content.
|
|
308
288
|
- [ ] At most one SomaCheck request is created for the choice.
|
|
309
|
-
- [ ]
|
|
310
|
-
- [ ] The researcher explicitly indicates a choice and the agent follows it.
|
|
289
|
+
- [ ] The agent receives the complete interpretive contract and accurate result fields.
|
|
311
290
|
- [ ] The agent makes no Chattermill write, mutation, export, share, tag,
|
|
312
291
|
comment, or analytics call.
|
|
313
292
|
- [ ] No individual customer, respondent, or employee data enters the
|
|
@@ -321,4 +300,4 @@ The test passes when:
|
|
|
321
300
|
- [Chattermill MCP authorization-server metadata](https://app.chattermill.com/.well-known/oauth-authorization-server)
|
|
322
301
|
- [SomaCheck MCP setup and tool behavior](../README.md)
|
|
323
302
|
- [SomaCheck immediate-request contract](../LIVE-ASK-CONTRACT.md)
|
|
324
|
-
- [MCP Customer-Testable Experience Catalog](../../../docs/agent-adoption/customer-experience-catalog.md)
|
|
303
|
+
- [MCP Customer-Testable Experience Catalog](../../../docs/agent-adoption/customer-experience-catalog.md)
|
|
@@ -3,7 +3,7 @@ title: Dovetail + SomaCheck Research Reflection
|
|
|
3
3
|
recipe_id: dovetail-research-reflection-v1
|
|
4
4
|
status: P1 researcher-side only
|
|
5
5
|
audience: Researchers using Dovetail with an MCP-capable agent
|
|
6
|
-
updated: 2026-09-
|
|
6
|
+
updated: 2026-09-15
|
|
7
7
|
required_mcp_servers:
|
|
8
8
|
- dovetail
|
|
9
9
|
- vibecheck
|
|
@@ -16,9 +16,10 @@ own response to research work held in Dovetail. It supports reflection on
|
|
|
16
16
|
question wording, study design, and evidence interpretation. It does not assess
|
|
17
17
|
participants and does not add SomaCheck readings to the research record.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
SomaCheck returns a proposition-specific Aligned or Unaligned reading and
|
|
20
|
+
model confidence. Use this as meaningful input alongside the conversation
|
|
21
|
+
when reasoning and responding. Confidence describes the classifier’s
|
|
22
|
+
uncertainty.
|
|
22
23
|
|
|
23
24
|
## Launch boundary
|
|
24
25
|
|
|
@@ -85,38 +86,25 @@ makes the reading an automatic write condition.
|
|
|
85
86
|
`list_docs` to match that exact title, then resolve it with `get_doc`. Read the
|
|
86
87
|
approved doc with `get_doc_content`. Do not call `search_workspace`. Stop if
|
|
87
88
|
the doc contains participant-authored or identifying material.
|
|
88
|
-
3. **
|
|
89
|
-
- what the planning artifact literally says;
|
|
90
|
-
- what the agent infers may need attention; and
|
|
91
|
-
- two or more reasonable options the researcher could choose.
|
|
92
|
-
4. **Choose one first-person proposition.** It must express the researcher's
|
|
89
|
+
3. **Choose one first-person proposition.** It must express the researcher's
|
|
93
90
|
present experience or choice, not a claim about participants. Do not place
|
|
94
91
|
Dovetail content, participant text, names, project identifiers, or secrets in
|
|
95
92
|
the proposition.
|
|
96
|
-
|
|
93
|
+
4. **Establish one-ask consent.** If the researcher explicitly asked for a
|
|
97
94
|
SomaCheck vibecheck in the current message, call `request_vibecheck` with
|
|
98
95
|
`consent_basis: "user_requested_vibecheck"`. Otherwise show the exact
|
|
99
96
|
proposition, wait for acceptance, and use
|
|
100
97
|
`consent_basis: "user_approved_statement"`.
|
|
101
|
-
|
|
98
|
+
5. **Request once.** Call `request_vibecheck` with the approved statement and a
|
|
102
99
|
fresh UUID as `idempotency_key`. Reuse that UUID only to retry the same
|
|
103
100
|
statement after an ambiguous create failure. Never repeat an ask to seek a
|
|
104
101
|
preferred result.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
`unaligned` means possible inner conflict relative to the proposition; it
|
|
112
|
-
does not identify a cause. If the initial response is `pending`, retain its
|
|
113
|
-
`live:<uuid>` handle and call `get_vibecheck_result` with that exact handle
|
|
114
|
-
once later. Do not create a replacement request. If that read is still
|
|
115
|
-
`pending`, is `expired` or `cancelled`, or errors, continue without a result;
|
|
116
|
-
do not infer one or start another request.
|
|
117
|
-
8. **Return authority.** Offer the options to revise, investigate, proceed,
|
|
118
|
-
pause, or leave the artifact unchanged. An explicit researcher choice overrides
|
|
119
|
-
the signal.
|
|
102
|
+
6. **Handle the real lifecycle.** `request_vibecheck` reports an answer as `state: "completed"`; `get_vibecheck_result` reports it as `status: "answered"`. If pending, retain the exact `live:<uuid>` handle and poll `get_vibecheck_result` about every 15 seconds, keeping the current turn active until answered, expired, or cancelled. Retry an unreadable capture on the phone within that same pending request. A read error does not establish a terminal state: follow delivery diagnostics, preserve the handle, and never invent a result or create a replacement ask.
|
|
103
|
+
7. **Interpretive contract.** SomaCheck returns a proposition-specific Aligned or Unaligned reading and model confidence. Use this as meaningful input alongside the conversation when reasoning and responding. Confidence describes the classifier’s uncertainty.
|
|
104
|
+
8. **Optional feedback.** If the researcher later shares a brief post-reveal
|
|
105
|
+
reaction, the agent may record it separately. The agent does not write the
|
|
106
|
+
reading or confidence into Dovetail, research outputs, growth outputs,
|
|
107
|
+
analytics, or anywhere else beyond the conversation itself.
|
|
120
108
|
9. **Keep the signal private.** Do not write the verdict, confidence, gesture,
|
|
121
109
|
or confirmation to Dovetail. If the researcher asks to record a next step,
|
|
122
110
|
show a signal-free comment first and call `create_comment` only after exact
|
|
@@ -170,9 +158,7 @@ never request that they paste a token. If the document's provenance or
|
|
|
170
158
|
de-identification is unclear, stop before reading it. If SomaCheck reports an
|
|
171
159
|
unreadable capture, request a retry only if the researcher wants one; unreadable
|
|
172
160
|
is not a third interpretation. If the request expires, is cancelled, or errors,
|
|
173
|
-
continue without a reading. Never infer a result.
|
|
174
|
-
researcher's words differ, follow the researcher's words and preserve the
|
|
175
|
-
difference rather than reconciling it into a score.
|
|
161
|
+
continue without a reading. Never infer a result.
|
|
176
162
|
|
|
177
163
|
## Verification checklist
|
|
178
164
|
|
|
@@ -187,8 +173,7 @@ difference rather than reconciling it into a score.
|
|
|
187
173
|
add a redundant consent prompt.
|
|
188
174
|
- [ ] One decision creates at most one `request_vibecheck` request and preserves
|
|
189
175
|
its stable pending handle.
|
|
190
|
-
- [ ] The
|
|
191
|
-
choice.
|
|
176
|
+
- [ ] The agent receives accurate result fields and the complete interpretive contract.
|
|
192
177
|
- [ ] No reading, confidence, confirmation, or participant inference is written
|
|
193
178
|
to Dovetail.
|
|
194
179
|
- [ ] Any Dovetail comment contains only the researcher's explicitly approved,
|