@somacheck/vibecheck 0.6.9 → 0.6.11
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 +84 -9
- package/claude-marketplace/.claude-plugin/marketplace.json +1 -1
- package/claude-marketplace/plugins/vibecheck/.claude-plugin/plugin.json +4 -4
- package/claude-marketplace/plugins/vibecheck/hooks/hooks.json +1 -1
- package/dist/cli.js +31 -0
- package/dist/client-setup.js +2 -1
- package/dist/constants.js +2 -1
- package/dist/readiness.js +2 -2
- package/dist/recipes.js +191 -0
- package/dist/server.js +58 -15
- package/package.json +4 -4
- package/recipes/chattermill-research-reflection.md +323 -0
- package/recipes/dovetail-research-reflection.md +204 -0
- package/recipes/great-question-research-reflection.md +212 -0
- package/recipes/maze-research-reflection.md +325 -0
- package/recipes/prolific-research-reflection.md +215 -0
- package/recipes/questionpro-research-reflection.md +151 -0
- package/recipes/spotify-listening-reflection.md +459 -0
- package/recipes/sprig-research-reflection.md +222 -0
- package/recipes/studio-somacheck-context.md +202 -0
- package/recipes/typeform-research-reflection.md +211 -0
- package/recipes/user-interviews-research-reflection.md +253 -0
package/dist/server.js
CHANGED
|
@@ -2,10 +2,22 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { StatementPendingError, } from "./vibecheck.js";
|
|
4
4
|
import { SomaCheckCompatibilityError, SomaCheckHttpError, SomaCheckLiveAskClientError, SomaCheckLiveAskConflictError, SomaCheckLiveAskPendingError, } from "./api.js";
|
|
5
|
-
import { PACKAGE_NAME, PACKAGE_SPEC, PACKAGE_VERSION } from "./constants.js";
|
|
6
|
-
|
|
5
|
+
import { PACKAGE_NAME, PACKAGE_SPEC, PACKAGE_VERSION, SOMACHECK_SETUP_URL } from "./constants.js";
|
|
6
|
+
// A human gesture (unlock phone, read the statement, make the wrist gesture)
|
|
7
|
+
// reliably takes longer than the old 10s budget, so an inline wait almost
|
|
8
|
+
// never resolves before the tool returns pending. The MCP SDK's documented
|
|
9
|
+
// default tool-call timeout is 60s (see LIVE-ASK-CONTRACT.md); 45s leaves a
|
|
10
|
+
// 15s margin for token loading, the create round trip, and result-read
|
|
11
|
+
// overhead on top of the sleeps below, while covering most prompt gestures
|
|
12
|
+
// inline. Overridable via `liveAskWait.budgetMs` so tests stay fast.
|
|
13
|
+
const LIVE_ASK_WAIT_BUDGET_MS = 45_000;
|
|
14
|
+
// Initial backoff, then steady 4s polling. The steady tail's length is sized
|
|
15
|
+
// so the sum matches LIVE_ASK_WAIT_BUDGET_MS above — the wait loop below
|
|
16
|
+
// stops as soon as either the array or the deadline is exhausted, so a
|
|
17
|
+
// smaller injected budgetMs (tests) still ends early on the deadline check.
|
|
7
18
|
const LIVE_ASK_POLL_DELAYS_MS = [
|
|
8
19
|
1_000, 1_500, 2_500, 4_000,
|
|
20
|
+
4_000, 4_000, 4_000, 4_000, 4_000, 4_000, 4_000, 4_000, 4_000,
|
|
9
21
|
];
|
|
10
22
|
const CLAUDE_CHANNEL_POLL_INTERVAL_MS = 2_000;
|
|
11
23
|
const CLAUDE_CHANNEL_MAX_WATCH_MS = 15 * 60 * 1_000;
|
|
@@ -59,8 +71,8 @@ const resultSchema = {
|
|
|
59
71
|
request_id: z.string(),
|
|
60
72
|
status: z.enum(["queued", "pending", "answered", "expired", "cancelled"]),
|
|
61
73
|
verdict: z.enum(["aligned", "unaligned"]).nullable(),
|
|
62
|
-
confidence: z.number().nullable(),
|
|
63
|
-
latency_s: z.number().nullable(),
|
|
74
|
+
confidence: z.number().min(0).max(1).nullable(),
|
|
75
|
+
latency_s: z.number().min(0).nullable(),
|
|
64
76
|
user_feedback: z.enum(["agreed", "disagreed", "not_provided"]).nullable().optional(),
|
|
65
77
|
};
|
|
66
78
|
const contextItemSchema = z.object({
|
|
@@ -70,7 +82,7 @@ const contextItemSchema = z.object({
|
|
|
70
82
|
confidence: z.number().min(0).max(1),
|
|
71
83
|
answered_at: z.string().datetime({ offset: true }),
|
|
72
84
|
});
|
|
73
|
-
const contextSchema = { checkins: z.array(contextItemSchema) };
|
|
85
|
+
const contextSchema = { checkins: z.array(contextItemSchema).max(20) };
|
|
74
86
|
const liveAskErrorCodeSchema = z.enum([
|
|
75
87
|
"link_revoked",
|
|
76
88
|
"upgrade_required",
|
|
@@ -107,9 +119,8 @@ const agentContextObservationSchema = z.object({
|
|
|
107
119
|
evidence_count: z.number().int().min(1).max(100).default(1),
|
|
108
120
|
});
|
|
109
121
|
const contextShareSchema = {
|
|
110
|
-
|
|
122
|
+
accepted: z.boolean(),
|
|
111
123
|
state: z.literal("ready"), observation_count: z.number().int().min(1).max(20),
|
|
112
|
-
captured_at: z.string().datetime({ offset: true }),
|
|
113
124
|
};
|
|
114
125
|
const SERVER_INSTRUCTIONS = [
|
|
115
126
|
"SomaCheck lets you ask your person for a vibecheck.",
|
|
@@ -215,17 +226,22 @@ export function createVibecheckServer(dependencies) {
|
|
|
215
226
|
outputSchema: contextShareSchema,
|
|
216
227
|
annotations: {
|
|
217
228
|
readOnlyHint: false,
|
|
218
|
-
destructiveHint:
|
|
219
|
-
idempotentHint:
|
|
229
|
+
destructiveHint: true,
|
|
230
|
+
idempotentHint: false,
|
|
220
231
|
openWorldHint: false,
|
|
221
232
|
},
|
|
222
233
|
}, async ({ observations }) => {
|
|
223
234
|
try {
|
|
224
235
|
const token = await dependencies.loadToken();
|
|
225
236
|
const result = await dependencies.api.shareContext(token, observations);
|
|
237
|
+
const structuredContent = {
|
|
238
|
+
accepted: result.accepted,
|
|
239
|
+
state: result.state,
|
|
240
|
+
observation_count: result.observation_count,
|
|
241
|
+
};
|
|
226
242
|
return {
|
|
227
243
|
content: [{ type: "text", text: `Shared ${result.observation_count} bounded context observation${result.observation_count === 1 ? "" : "s"}. SomaCheck can now prepare contextual propositions.` }],
|
|
228
|
-
structuredContent
|
|
244
|
+
structuredContent,
|
|
229
245
|
};
|
|
230
246
|
}
|
|
231
247
|
catch (error) {
|
|
@@ -298,7 +314,9 @@ export function createVibecheckServer(dependencies) {
|
|
|
298
314
|
: result.status === "queued"
|
|
299
315
|
? "Queued. The proposition is cached until the person advances their feed."
|
|
300
316
|
: result.status === "pending"
|
|
301
|
-
?
|
|
317
|
+
? liveRequestId === null
|
|
318
|
+
? "Pending. The person has not completed this check-in yet."
|
|
319
|
+
: pendingWaitText(request_id, null, false)
|
|
302
320
|
: `The request is ${result.status}.`;
|
|
303
321
|
return { content: [{ type: "text", text }], structuredContent };
|
|
304
322
|
}
|
|
@@ -308,7 +326,8 @@ export function createVibecheckServer(dependencies) {
|
|
|
308
326
|
});
|
|
309
327
|
server.registerTool("request_vibecheck", {
|
|
310
328
|
title: "Request a Vibecheck",
|
|
311
|
-
description: "Send one statement to the person's phone for a SomaCheck vibecheck. If the person asks for a vibecheck, choose a useful first-person statement from your available context and send it. For proactive offers, call only after the person accepts. The result is context, not authorization."
|
|
329
|
+
description: "Send one statement to the person's phone for a SomaCheck vibecheck. If the person asks for a vibecheck, choose a useful first-person statement from your available context and send it. For proactive offers, call only after the person accepts. The result is context, not authorization. "
|
|
330
|
+
+ "This call waits up to 45 seconds. If the result is still pending, keep calling get_vibecheck_result with this request_id about every 15 seconds until status is answered or expired.",
|
|
312
331
|
inputSchema: {
|
|
313
332
|
statement: z.string().trim().min(1).max(1000)
|
|
314
333
|
.describe("One plain-language first-person statement for the person to test. Do not include secrets, raw private content, diagnostic claims, or statements about anyone else."),
|
|
@@ -320,7 +339,7 @@ export function createVibecheckServer(dependencies) {
|
|
|
320
339
|
outputSchema: liveAskSchema,
|
|
321
340
|
annotations: {
|
|
322
341
|
readOnlyHint: false,
|
|
323
|
-
destructiveHint:
|
|
342
|
+
destructiveHint: true,
|
|
324
343
|
idempotentHint: true,
|
|
325
344
|
openWorldHint: false,
|
|
326
345
|
},
|
|
@@ -367,8 +386,11 @@ export function createVibecheckServer(dependencies) {
|
|
|
367
386
|
? `Vibecheck completed: ${result.verdict}, confidence ${formatChannelConfidence(result.confidence)}${formatUserFeedback(result.user_feedback)}.`
|
|
368
387
|
: result.state === "pending"
|
|
369
388
|
? result.delivery_state === "failed" || result.delivery_state === "skipped"
|
|
370
|
-
? `Vibecheck ${result.request_id} was stored, but phone delivery is ${result.delivery_state}.
|
|
371
|
-
|
|
389
|
+
? `Vibecheck ${result.request_id} was stored, but phone delivery is ${result.delivery_state}. `
|
|
390
|
+
+ `Open SomaCheck on your phone, signed into the same account, and check Home. `
|
|
391
|
+
+ `If you do not have the app, install it at ${SOMACHECK_SETUP_URL} and finish phone setup. `
|
|
392
|
+
+ `Keep this request ID; do not create a duplicate or claim the phone received it. It expires at ${result.expires_at}.`
|
|
393
|
+
: pendingWaitText(result.request_id, result.expires_at, supportsClaudeChannel)
|
|
372
394
|
: `This vibecheck is ${result.state}.`;
|
|
373
395
|
return { content: [{ type: "text", text }], structuredContent };
|
|
374
396
|
}
|
|
@@ -455,6 +477,27 @@ function formatChannelConfidence(confidence) {
|
|
|
455
477
|
function formatUserFeedback(feedback) {
|
|
456
478
|
return feedback === undefined || feedback === null ? "" : `, user feedback: ${feedback}`;
|
|
457
479
|
}
|
|
480
|
+
/** Text for a still-pending Live Ask, whether just created (delivery not
|
|
481
|
+
* failed/skipped) or re-read via get_vibecheck_result. `expiresAt` is null
|
|
482
|
+
* when the caller has no expiry to report (get_vibecheck_result's live read
|
|
483
|
+
* does not receive one). `hasLocalWatch` is true only for the local stdio
|
|
484
|
+
* server when the Claude PostToolUse hook's background watch is armed for
|
|
485
|
+
* this request (see startClaudeChannelWatch) — that watch notifies the same
|
|
486
|
+
* session automatically, so the agent should not be told to poll. The hosted
|
|
487
|
+
* edge function is stateless per request and never has a watch, so it must
|
|
488
|
+
* tell the agent exactly what to do next instead of inviting it to ask the
|
|
489
|
+
* human. */
|
|
490
|
+
function pendingWaitText(requestId, expiresAt, hasLocalWatch) {
|
|
491
|
+
if (hasLocalWatch) {
|
|
492
|
+
return `Vibecheck ${requestId} is pending. It expires at ${expiresAt}. This session will be notified automatically when the person responds.`;
|
|
493
|
+
}
|
|
494
|
+
return `Vibecheck ${requestId} is pending. SomaCheck has not received a response yet. `
|
|
495
|
+
+ `If no phone prompt appears, open SomaCheck on the same account; phone setup is at ${SOMACHECK_SETUP_URL}. `
|
|
496
|
+
+ `Call get_vibecheck_result with request_id ${requestId} again in about 15 seconds, `
|
|
497
|
+
+ `and keep polling at that cadence until the state is answered or expired. `
|
|
498
|
+
+ `Do not end your turn or ask the person whether to check; they are answering on their phone.`
|
|
499
|
+
+ (expiresAt === null ? "" : ` It expires at ${expiresAt}.`);
|
|
500
|
+
}
|
|
458
501
|
async function waitForLiveVibecheck(created, token, identity, dependencies, signal, deadline, progress) {
|
|
459
502
|
if (created.state !== "pending"
|
|
460
503
|
|| created.delivery_state === "failed"
|
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@somacheck/vibecheck",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11",
|
|
4
4
|
"mcpName": "io.github.Sensie-agents/vibecheck",
|
|
5
5
|
"description": "Send a consented vibecheck to SomaCheck and use the result as a signal.",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/
|
|
9
|
-
"directory": "packages/vibecheck"
|
|
8
|
+
"url": "git+https://github.com/Sensie-agents/vibecheck.git"
|
|
10
9
|
},
|
|
11
10
|
"type": "module",
|
|
12
11
|
"bin": {
|
|
@@ -18,6 +17,7 @@
|
|
|
18
17
|
"files": [
|
|
19
18
|
"dist/*.js",
|
|
20
19
|
"SKILL.md",
|
|
20
|
+
"recipes/**",
|
|
21
21
|
"claude-marketplace/**"
|
|
22
22
|
],
|
|
23
23
|
"scripts": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"tsx": "^4.23.1",
|
|
41
41
|
"typescript": "^5.9.3"
|
|
42
42
|
},
|
|
43
|
-
"license": "
|
|
43
|
+
"license": "MIT",
|
|
44
44
|
"publishConfig": {
|
|
45
45
|
"access": "public"
|
|
46
46
|
}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Chattermill + SomaCheck Research Reflection
|
|
3
|
+
recipe_id: chattermill-research-reflection-v1
|
|
4
|
+
status: P1 researcher-side read-only reflection; aggregate metrics and generated highlights only
|
|
5
|
+
audience: Researchers and CX analysts using Chattermill with an MCP-capable agent
|
|
6
|
+
updated: 2026-09-05
|
|
7
|
+
required_mcp_servers:
|
|
8
|
+
- chattermill
|
|
9
|
+
- vibecheck
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Chattermill + SomaCheck Research Reflection
|
|
13
|
+
|
|
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
|
|
17
|
+
only aggregate metrics or generated highlights and performs no Chattermill
|
|
18
|
+
write.
|
|
19
|
+
|
|
20
|
+
Chattermill exposes a hosted read-only MCP surface
|
|
21
|
+
(`https://app.chattermill.com/mcp`, Streamable HTTP). Its published OAuth
|
|
22
|
+
protected-resource metadata advertises only the `mcp:read` scope alongside
|
|
23
|
+
`openid profile email offline_access`; there is no write scope, and the server
|
|
24
|
+
is documented as read-only. Existing Chattermill roles and permissions still
|
|
25
|
+
apply, so an agent never sees data the researcher cannot already view.
|
|
26
|
+
|
|
27
|
+
The SomaCheck reading is context for the researcher's own interpretation or
|
|
28
|
+
next step. It is not truth, diagnosis, authorization, evidence about any
|
|
29
|
+
individual customer, or a research decision. The researcher remains the
|
|
30
|
+
authority.
|
|
31
|
+
|
|
32
|
+
## Capability and access boundary
|
|
33
|
+
|
|
34
|
+
Chattermill documents the hosted MCP as read-only and OAuth 2.1 authenticated
|
|
35
|
+
at `https://app.chattermill.com/mcp`. A product or integration owner provides
|
|
36
|
+
an organization-authorized connection or partner sandbox that can already
|
|
37
|
+
view the named project's aggregate metrics and highlights. The provisioned MCP
|
|
38
|
+
client completes Chattermill's OAuth flow at `auth.chattermill.com`. Running
|
|
39
|
+
this recipe never asks the researcher to sign up, purchase access, administer
|
|
40
|
+
access, or populate Chattermill data.
|
|
41
|
+
|
|
42
|
+
The MCP server's protected-resource metadata advertises `mcp:read` as its only
|
|
43
|
+
MCP scope. There is no write tool in the published contract. If a write
|
|
44
|
+
surface appears at runtime anyway, this recipe still performs no write.
|
|
45
|
+
|
|
46
|
+
This P1 recipe is researcher-side only. Do not use it to:
|
|
47
|
+
|
|
48
|
+
- ask a customer, respondent, employee, candidate, patient, or student to
|
|
49
|
+
complete a SomaCheck;
|
|
50
|
+
- retrieve individual feedback responses, verbatim comments, source quotes,
|
|
51
|
+
respondent identifiers, contact fields, or any free-text feedback;
|
|
52
|
+
- retrieve observation clusters that surface representative verbatim snippets
|
|
53
|
+
or observation identifiers tied to individual feedback;
|
|
54
|
+
- use a reading for customer targeting, eligibility, payment, ranking,
|
|
55
|
+
churn scoring, authenticity, quality, or performance evaluation;
|
|
56
|
+
- write a SomaCheck reading, confidence, gesture, confirmation, or hidden
|
|
57
|
+
score into Chattermill, a dashboard, a tag, a theme, a highlight, an
|
|
58
|
+
insight, or any analytics record; or
|
|
59
|
+
- create, edit, publish, delete, share, export, or otherwise mutate any
|
|
60
|
+
Chattermill project, theme, attribute, metric, dashboard, or insight.
|
|
61
|
+
|
|
62
|
+
Participant- or customer-facing use stays blocked until a `respondent_private`
|
|
63
|
+
architecture prevents researcher or platform access to individual readings,
|
|
64
|
+
methodology and ethics review is complete, and Sensie gives explicit
|
|
65
|
+
privacy/legal approval. Chattermill MCP access, read-only scope, or participant
|
|
66
|
+
consent alone does not satisfy these gates.
|
|
67
|
+
|
|
68
|
+
## Experience a researcher can run
|
|
69
|
+
|
|
70
|
+
The researcher holds the phone and selects one existing Chattermill project
|
|
71
|
+
supplied by the workspace owner, then provides one research question. The agent
|
|
72
|
+
reads only that project's aggregate metrics (`get_metrics`) or generated
|
|
73
|
+
highlights (`generate_highlights`), plus the discovery metadata needed to build
|
|
74
|
+
the query (`list_themes`, `list_attributes`, `search_attributes`,
|
|
75
|
+
`list_metric_options`). It never calls `get_feedback` (individual responses)
|
|
76
|
+
or `search_observations` (observation identifiers and representative verbatim
|
|
77
|
+
snippets), and never surfaces individual feedback, source quotes, respondent
|
|
78
|
+
identifiers, or free text. The agent offers two defensible interpretations of
|
|
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's
|
|
81
|
+
typed choice, the agent stops. There is no Chattermill write.
|
|
82
|
+
|
|
83
|
+
## Starter prompt
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
Use Chattermill and SomaCheck to help me reflect on my own interpretation of
|
|
87
|
+
customer-feedback insights for one named Chattermill project. I am the
|
|
88
|
+
researcher and I am holding the phone. The project is:
|
|
89
|
+
[PROJECT NAME OR ID PROVIDED BY THE WORKSPACE OWNER]. Use only the
|
|
90
|
+
preconfigured official hosted read-only Chattermill MCP at
|
|
91
|
+
https://app.chattermill.com/mcp. Read only aggregate metrics (get_metrics) or
|
|
92
|
+
generated highlights (generate_highlights), plus discovery metadata
|
|
93
|
+
(list_themes, list_attributes, search_attributes, list_metric_options) needed
|
|
94
|
+
to build the query. Never call get_feedback or search_observations. Never
|
|
95
|
+
surface individual feedback, source quotes, respondent identifiers, contact
|
|
96
|
+
fields, or free text. Propose two defensible interpretations of what the
|
|
97
|
+
aggregate signal suggests, with the tradeoff for each. Then offer one short
|
|
98
|
+
first-person proposition about my own interpretation or next step and wait
|
|
99
|
+
for my acceptance before any proactive SomaCheck ask. Treat Aligned or
|
|
100
|
+
Unaligned plus confidence as context, not truth, diagnosis, authorization, or
|
|
101
|
+
evidence about any individual customer. Ask what I choose in words. Keep the
|
|
102
|
+
proposition, reading, confidence, and my confirmation out of Chattermill. Do
|
|
103
|
+
not write to Chattermill; the hosted MCP is read-only. If the preconfigured
|
|
104
|
+
connection, mcp:read, or a safe aggregate tool is absent, return the copy-ready
|
|
105
|
+
interpretation and tell the integration owner what capability is missing. Do
|
|
106
|
+
not ask me to sign up, purchase access, administer access, or populate
|
|
107
|
+
Chattermill data.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Prerequisites and account access
|
|
111
|
+
|
|
112
|
+
1. The researcher can complete a normal SomaCheck request through the connected
|
|
113
|
+
`vibecheck` MCP server (`request_vibecheck` and
|
|
114
|
+
`get_vibecheck_result`).
|
|
115
|
+
2. A product or integration owner has provisioned an authorized organization
|
|
116
|
+
connection or partner sandbox that can already view the named project's
|
|
117
|
+
aggregate metrics and highlights. Existing Chattermill roles and permissions
|
|
118
|
+
apply; the MCP grants no access beyond the provisioned workspace.
|
|
119
|
+
3. The provisioned MCP client is connected to exactly
|
|
120
|
+
`https://app.chattermill.com/mcp` and authenticated through the OAuth 2.1
|
|
121
|
+
flow Chattermill publishes at `auth.chattermill.com`. Do not substitute a
|
|
122
|
+
different host, path, or non-Chattermill endpoint.
|
|
123
|
+
4. The workspace owner supplies one named existing Chattermill project (by name
|
|
124
|
+
or ID) whose aggregate metrics or generated highlights are enough for the
|
|
125
|
+
reflection task, with no need for individual responses or verbatim comments.
|
|
126
|
+
The researcher is not responsible for creating or populating it.
|
|
127
|
+
5. No credential, OAuth token, API key, project ID containing PII, or
|
|
128
|
+
respondent identifier appears in chat, logs, prompts, or the SomaCheck
|
|
129
|
+
proposition.
|
|
130
|
+
|
|
131
|
+
## Chattermill tool policy
|
|
132
|
+
|
|
133
|
+
Chattermill publishes a hosted, read-only MCP (`mcp:read` scope). It documents
|
|
134
|
+
eight tools in two groups: discovery tools and data tools. The agent must
|
|
135
|
+
inspect the live tool list at runtime and use only the smallest tools that
|
|
136
|
+
return aggregate or generated output. Treat the live schemas as
|
|
137
|
+
runtime-discovered; do not assume undocumented tool names.
|
|
138
|
+
|
|
139
|
+
| Purpose | Allowed surface | Rule |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| Discover theme/category names | `list_themes` | Metadata only; names configured in the project, never feedback content |
|
|
142
|
+
| Discover filterable attributes | `list_attributes` | Metadata only; attribute names, never feedback values |
|
|
143
|
+
| Discover valid filter values | `search_attributes` | Metadata only for segment attributes (country, product, data source); never enumerate an identifier-bearing attribute such as email or customer ID |
|
|
144
|
+
| Discover metric options | `list_metric_options` | Metadata only; chart types, metrics, frequencies, breakdown options |
|
|
145
|
+
| Read aggregate quantitative data | `get_metrics` | Aggregate NPS, sentiment, volume, or trend metrics only |
|
|
146
|
+
| Read generated highlights | `generate_highlights` | AI-generated summary of themes, issues, or trends only |
|
|
147
|
+
| Read individual feedback responses | `get_feedback` | Prohibited in this recipe |
|
|
148
|
+
| Search observation clusters with snippets | `search_observations` | Prohibited in this recipe; returns observation identifiers and representative verbatim snippets |
|
|
149
|
+
| Any create, edit, publish, delete, share, export, or mutation | any write or mutation tool | Prohibited in this recipe |
|
|
150
|
+
| Private reflection | `request_vibecheck`, optional `get_vibecheck_result` | One decision, one request, one stable pending handle |
|
|
151
|
+
|
|
152
|
+
Do not broaden a connection to a root or all-tools endpoint to make this
|
|
153
|
+
recipe work. If the runtime does not expose `get_metrics` or
|
|
154
|
+
`generate_highlights`, label the Chattermill half untested and stop.
|
|
155
|
+
|
|
156
|
+
## Exact cross-MCP workflow
|
|
157
|
+
|
|
158
|
+
1. **Confirm the subject and scope.** State that the researcher is the phone
|
|
159
|
+
holder and the subject of the proposition. Name the one existing Chattermill
|
|
160
|
+
project supplied by the workspace owner (by name or ID) and the research
|
|
161
|
+
question.
|
|
162
|
+
2. **Verify access honestly.** Confirm the client is connected to exactly
|
|
163
|
+
`https://app.chattermill.com/mcp` through Chattermill's OAuth flow and
|
|
164
|
+
that the `mcp:read` scope is present. Do not claim a live pass on
|
|
165
|
+
documentation, a fixture, or a connection that lacks the read scope.
|
|
166
|
+
3. **Read only aggregate or generated output.** Call `get_metrics` or
|
|
167
|
+
`generate_highlights`, using discovery metadata (`list_themes`,
|
|
168
|
+
`list_attributes`, `search_attributes`, `list_metric_options`) only to
|
|
169
|
+
build the query. Do not call `get_feedback` or `search_observations`, and
|
|
170
|
+
do not surface individual feedback, source quotes, respondent identifiers,
|
|
171
|
+
contact fields, or free text.
|
|
172
|
+
4. **Separate evidence from interpretation.** Label what the aggregate
|
|
173
|
+
metrics or highlights literally contain, what the agent infers, and what
|
|
174
|
+
the researcher's own judgment must still decide.
|
|
175
|
+
5. **Propose two interpretations.** Offer two defensible interpretations of
|
|
176
|
+
what the aggregate signal suggests, with the tradeoff for each.
|
|
177
|
+
6. **Choose one first-person proposition.** It must be short, first-person,
|
|
178
|
+
and about the researcher's own interpretation or next step. Do not include
|
|
179
|
+
project IDs containing PII, individual feedback, source quotes,
|
|
180
|
+
respondent identifiers, contact fields, or free text.
|
|
181
|
+
7. **Establish one-ask consent.** If the researcher explicitly requested a
|
|
182
|
+
SomaCheck check-in in the current message, use
|
|
183
|
+
`consent_basis: "user_requested_vibecheck"`. Otherwise show the exact
|
|
184
|
+
proposition, wait for acceptance, and use
|
|
185
|
+
`consent_basis: "user_approved_statement"`.
|
|
186
|
+
8. **Request once.** Call `request_vibecheck` with the approved statement and
|
|
187
|
+
a fresh UUID `idempotency_key`. Reuse it only to retry the identical
|
|
188
|
+
request after an ambiguous create failure.
|
|
189
|
+
9. **Handle the real lifecycle.** A terminal response from
|
|
190
|
+
`request_vibecheck` uses `state: "completed"`; a later read uses
|
|
191
|
+
`status: "answered"`. If the initial response is `pending`, retain the
|
|
192
|
+
returned `live:<uuid>` handle and call `get_vibecheck_result` with it once
|
|
193
|
+
later. If it is still `pending`, `expired`, `cancelled`, or errors,
|
|
194
|
+
continue without a result. Never issue a replacement ask.
|
|
195
|
+
10. **Return authority.** Separate observation (a valid gesture was
|
|
196
|
+
captured), interpretation (`aligned` or `unaligned` plus model
|
|
197
|
+
confidence), confirmation (what the researcher says), and choice (what the
|
|
198
|
+
researcher decides). `unaligned` may indicate possible inner conflict
|
|
199
|
+
relative to the proposition; it does not name a cause, choose an
|
|
200
|
+
interpretation, or justify a research claim.
|
|
201
|
+
11. **Ask for words.** Ask the researcher which interpretation they choose,
|
|
202
|
+
whether they want a third option, or whether they want to stop. Follow
|
|
203
|
+
their typed choice.
|
|
204
|
+
12. **Keep the signal out of Chattermill.** Do not call any write, mutation,
|
|
205
|
+
export, tag, theme, highlight, insight, or analytics tool. Do not store
|
|
206
|
+
the proposition, reading, confidence, confirmation, or gesture metadata
|
|
207
|
+
in Chattermill.
|
|
208
|
+
13. **End the test.** Report the exact Chattermill tool names used and the
|
|
209
|
+
exact aggregate or generated fields returned, or report the copy-ready
|
|
210
|
+
interpretation when the Chattermill half is untested. Do not create,
|
|
211
|
+
edit, publish, delete, share, or export any Chattermill object.
|
|
212
|
+
|
|
213
|
+
## Proposition examples
|
|
214
|
+
|
|
215
|
+
Allowed:
|
|
216
|
+
|
|
217
|
+
- "I can interpret this aggregate signal without reading individual
|
|
218
|
+
feedback."
|
|
219
|
+
- "I have a clear reading of what these aggregate metrics suggest."
|
|
220
|
+
- "I am ready to act on generated highlights without opening verbatim
|
|
221
|
+
comments."
|
|
222
|
+
- "I want to keep individual responses out of my interpretation."
|
|
223
|
+
- "I can state what this trend does and does not show."
|
|
224
|
+
|
|
225
|
+
Not allowed:
|
|
226
|
+
|
|
227
|
+
- "This customer is telling the truth."
|
|
228
|
+
- "These respondents are engaged."
|
|
229
|
+
- "This feedback proves the feature is broken."
|
|
230
|
+
- "This customer should be flagged or deprioritized."
|
|
231
|
+
- "This aggregate metric validates my hypothesis."
|
|
232
|
+
|
|
233
|
+
## Visible success condition
|
|
234
|
+
|
|
235
|
+
The test passes when:
|
|
236
|
+
|
|
237
|
+
- the researcher selects one existing Chattermill project supplied by a
|
|
238
|
+
workspace owner whose provisioned connection can view its aggregate metrics
|
|
239
|
+
and highlights;
|
|
240
|
+
- the agent reads only aggregate metrics (`get_metrics`) or generated
|
|
241
|
+
highlights (`generate_highlights`) through the hosted read-only
|
|
242
|
+
Chattermill MCP and reports the exact tool names used;
|
|
243
|
+
- the agent never calls `get_feedback` or `search_observations`, and never
|
|
244
|
+
surfaces individual feedback, source quotes, respondent identifiers,
|
|
245
|
+
contact fields, or free text;
|
|
246
|
+
- the researcher receives at most one optional SomaCheck check-in on their
|
|
247
|
+
phone on an exact first-person proposition about their own interpretation or
|
|
248
|
+
next step;
|
|
249
|
+
- the agent presents the result as context, asks the researcher to state their
|
|
250
|
+
choice in words, and follows the typed choice;
|
|
251
|
+
- the agent makes no Chattermill write, mutation, export, share, tag,
|
|
252
|
+
comment, or analytics call; and
|
|
253
|
+
- no individual customer, respondent, or employee data enters the cross-MCP
|
|
254
|
+
flow.
|
|
255
|
+
|
|
256
|
+
## Failure and degraded paths
|
|
257
|
+
|
|
258
|
+
- **Read scope absent or OAuth failure:** Report the missing capability to the
|
|
259
|
+
product or integration owner; never request a token or API key in chat or ask
|
|
260
|
+
the researcher to sign up, purchase access, or administer access. Label the
|
|
261
|
+
Chattermill half untested, return the copy-ready interpretation, and stop.
|
|
262
|
+
- **Runtime exposes only individual-response or observation tools:** Do not
|
|
263
|
+
call them. Label the Chattermill half untested and stop. Do not broaden
|
|
264
|
+
the connection to make this recipe work.
|
|
265
|
+
- **MCP returns individual feedback, source quotes, respondent identifiers,
|
|
266
|
+
contact fields, or free text:** Stop immediately. Discard the individual
|
|
267
|
+
content from the agent context. Report the exact fields received and ask the
|
|
268
|
+
product or integration owner to revoke or limit the scope. Do not use the
|
|
269
|
+
individual content in any proposition, framing, or interpretation.
|
|
270
|
+
- **SomaCheck unresolved:** Use the same `live:<uuid>` handle once later,
|
|
271
|
+
then proceed without a reading if still unresolved.
|
|
272
|
+
- **Unreadable capture:** Offer a retry only if the researcher wants it;
|
|
273
|
+
unreadable is not a third interpretation.
|
|
274
|
+
- **Researcher disagrees with the reading:** Follow the researcher's typed
|
|
275
|
+
choice without reconciliation or repetition.
|
|
276
|
+
|
|
277
|
+
## Privacy boundary
|
|
278
|
+
|
|
279
|
+
- Raw phone motion never reaches Chattermill or the agent.
|
|
280
|
+
- Chattermill receives no proposition, reading, confidence, confirmation,
|
|
281
|
+
gesture metadata, or SomaCheck output through this recipe.
|
|
282
|
+
- SomaCheck receives no Chattermill project ID containing PII, individual
|
|
283
|
+
feedback, source quote, respondent identifier, contact field, free-text
|
|
284
|
+
comment, or verbatim response.
|
|
285
|
+
- No individual customer, respondent, or employee data is read into the agent
|
|
286
|
+
conversation, prompt, proposition, framing, or memory.
|
|
287
|
+
- The Chattermill project remains unchanged. No write, mutation, export,
|
|
288
|
+
share, tag, comment, insight, or analytics call occurs.
|
|
289
|
+
|
|
290
|
+
## Test checklist
|
|
291
|
+
|
|
292
|
+
- [ ] The researcher is the phone holder and the subject of the proposition.
|
|
293
|
+
- [ ] The client is connected to exactly `https://app.chattermill.com/mcp`
|
|
294
|
+
through Chattermill's OAuth 2.1 flow; no other host, path, or
|
|
295
|
+
non-Chattermill endpoint is used.
|
|
296
|
+
- [ ] The `mcp:read` scope is present and no write scope is assumed.
|
|
297
|
+
- [ ] No Chattermill credential, OAuth token, or API key appears in chat,
|
|
298
|
+
logs, or the SomaCheck proposition.
|
|
299
|
+
- [ ] The agent reads only `get_metrics` or `generate_highlights`, using
|
|
300
|
+
discovery metadata only to build the query, and reports the exact
|
|
301
|
+
runtime tool names used.
|
|
302
|
+
- [ ] The agent does not call `get_feedback` or `search_observations`.
|
|
303
|
+
- [ ] No individual feedback, source quote, respondent identifier, contact
|
|
304
|
+
field, or free text appears in the conversation, proposition, or
|
|
305
|
+
interpretation.
|
|
306
|
+
- [ ] The proposition is first-person and contains no Chattermill content.
|
|
307
|
+
- [ ] At most one SomaCheck request is created for the choice.
|
|
308
|
+
- [ ] Observation, interpretation, confirmation, and choice remain separate.
|
|
309
|
+
- [ ] The researcher states a choice in words and the agent follows the typed choice.
|
|
310
|
+
- [ ] The agent makes no Chattermill write, mutation, export, share, tag,
|
|
311
|
+
comment, or analytics call.
|
|
312
|
+
- [ ] No individual customer, respondent, or employee data enters the
|
|
313
|
+
cross-MCP flow.
|
|
314
|
+
|
|
315
|
+
## Sources
|
|
316
|
+
|
|
317
|
+
- [Chattermill MCP server guide](https://docs.chattermill.com/en/articles/13943134-chattermill-mcp-server)
|
|
318
|
+
- [Chattermill MCP endpoint](https://app.chattermill.com/mcp)
|
|
319
|
+
- [Chattermill MCP protected-resource metadata](https://app.chattermill.com/.well-known/oauth-protected-resource/mcp)
|
|
320
|
+
- [Chattermill MCP authorization-server metadata](https://app.chattermill.com/.well-known/oauth-authorization-server)
|
|
321
|
+
- [SomaCheck MCP setup and tool behavior](../README.md)
|
|
322
|
+
- [SomaCheck immediate-request contract](../LIVE-ASK-CONTRACT.md)
|
|
323
|
+
- [MCP Customer-Testable Experience Catalog](../../../docs/agent-adoption/customer-experience-catalog.md)
|