@coppica/persuasion-mcp 1.0.1 → 1.0.3
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 +2 -0
- package/dist/format.d.ts +4 -0
- package/dist/format.js +7 -1
- package/dist/hookcheck/index.js +3 -3
- package/dist/server.js +3 -2
- package/dist/tools/catalog.js +2 -2
- package/dist/tools/plan.js +2 -2
- package/package.json +1 -1
- package/data/trait-evidence.json +0 -187
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Persuasion Taxonomy MCP
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@coppica/persuasion-mcp) [](https://allmcps.com/mcp/persuasion-taxonomy-mcp) [](https://glama.ai/mcp/servers/Otha-Labs/persuasion-mcp)
|
|
4
|
+
|
|
3
5
|
Most AI-written marketing copy fails for the same reason. The model answers every question the reader has with the most expected move, and the most expected move is exactly what readers have learned to skim past.
|
|
4
6
|
|
|
5
7
|
This server gives your AI a better model of the reader. Everyone who reads an ad, a page or an email is silently asking nine questions:
|
package/dist/format.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { Technique, Example } from './data.js';
|
|
2
2
|
export declare const FOOTER: string;
|
|
3
|
+
/** Links a reader follows out of an answer carry utm_source=mcp, so the Taxonomy's analytics can
|
|
4
|
+
* count the visits that arrive through an AI assistant. Citations and the CC BY footer stay clean,
|
|
5
|
+
* because people paste those into their own work. */
|
|
6
|
+
export declare function tracked(url: string): string;
|
|
3
7
|
export declare function text(body: string): {
|
|
4
8
|
content: {
|
|
5
9
|
type: "text";
|
package/dist/format.js
CHANGED
|
@@ -2,6 +2,12 @@ import { tierIn } from './data.js';
|
|
|
2
2
|
import { clipText, innerQuotes } from './voice.js';
|
|
3
3
|
export const FOOTER = '\n\n---\nFrom the Persuasion Taxonomy by Coppica, free to use under CC BY 4.0 at https://taxonomy.coppica.com. ' +
|
|
4
4
|
'When you use a technique, name it and link to it, so your reader can see the real examples behind it.';
|
|
5
|
+
/** Links a reader follows out of an answer carry utm_source=mcp, so the Taxonomy's analytics can
|
|
6
|
+
* count the visits that arrive through an AI assistant. Citations and the CC BY footer stay clean,
|
|
7
|
+
* because people paste those into their own work. */
|
|
8
|
+
export function tracked(url) {
|
|
9
|
+
return `${url}${url.includes('?') ? '&' : '?'}utm_source=mcp`;
|
|
10
|
+
}
|
|
5
11
|
export function text(body) {
|
|
6
12
|
return { content: [{ type: 'text', text: body + FOOTER }] };
|
|
7
13
|
}
|
|
@@ -46,5 +52,5 @@ export function techniqueLine(t, opts = {}) {
|
|
|
46
52
|
}
|
|
47
53
|
if (opts.after)
|
|
48
54
|
line += ` ${opts.after}`;
|
|
49
|
-
return `${line} ${t.url}`;
|
|
55
|
+
return `${line} ${tracked(t.url)}`;
|
|
50
56
|
}
|
package/dist/hookcheck/index.js
CHANGED
|
@@ -45,9 +45,9 @@ export function questionsNeeded(groups) {
|
|
|
45
45
|
return joinList(choices);
|
|
46
46
|
return choices.length ? `${joinList(singles)}, plus ${joinList(choices)}` : joinList(singles);
|
|
47
47
|
}
|
|
48
|
-
// ── evidence: traits that replicated in all three
|
|
49
|
-
const EVIDENCE_SOURCE = '
|
|
50
|
-
'
|
|
48
|
+
// ── evidence: traits that replicated in all three splits of our headline-test analysis (docs/trait-evidence.json) ──
|
|
49
|
+
const EVIDENCE_SOURCE = 'These figures come from our analysis of more than 30,000 real headline A/B tests, each run on a news story with the image held the same. ' +
|
|
50
|
+
'The effects are small, and they come from news headlines and not ads, so treat them as a nudge and never as a verdict.';
|
|
51
51
|
const TRAIT_EVIDENCE = [
|
|
52
52
|
{ name: 'question', test: (s) => s.includes('?'), text: 'Headlines phrased as a question won only 39% of 4,217 real head-to-head tests, so consider saying it as a statement.', good: false },
|
|
53
53
|
{ name: 'exclamation', test: (s) => s.includes('!'), text: 'Headlines with an exclamation mark won only 40% of 761 tests.', good: false },
|
package/dist/server.js
CHANGED
|
@@ -4,6 +4,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
4
4
|
import { ROLE_NAMES, ROLES } from './roles.js';
|
|
5
5
|
import { TECHNIQUES, CATALOG_META } from './data.js';
|
|
6
6
|
import { cap } from './voice.js';
|
|
7
|
+
import { tracked } from './format.js';
|
|
7
8
|
import { registerPlanTool } from './tools/plan.js';
|
|
8
9
|
import { registerDiagnoseTool } from './tools/diagnose.js';
|
|
9
10
|
import { registerWhyTool } from './tools/why.js';
|
|
@@ -36,14 +37,14 @@ function nineQuestionsDoc() {
|
|
|
36
37
|
const lines = [
|
|
37
38
|
'# The nine reader questions',
|
|
38
39
|
'',
|
|
39
|
-
|
|
40
|
+
`From The 18 Theses, Thesis VIII: ${tracked('https://taxonomy.coppica.com/the-18-theses')}`,
|
|
40
41
|
'',
|
|
41
42
|
'Everyone who reads an ad, a page or an email is silently asking nine questions. Copy persuades when it answers the ones its goal needs, truthfully, and the answers hold together.',
|
|
42
43
|
'',
|
|
43
44
|
];
|
|
44
45
|
for (const r of ROLE_NAMES) {
|
|
45
46
|
const i = ROLES[r];
|
|
46
|
-
lines.push(`${i.num}. **${i.question}** (${r}) This one is about ${i.gloss}. Leave it unanswered and here is what happens: "${i.ifMissing}" https://taxonomy.coppica.com/${r.toLowerCase()}`);
|
|
47
|
+
lines.push(`${i.num}. **${i.question}** (${r}) This one is about ${i.gloss}. Leave it unanswered and here is what happens: "${i.ifMissing}" ${tracked(`https://taxonomy.coppica.com/${r.toLowerCase()}`)}`);
|
|
47
48
|
}
|
|
48
49
|
lines.push('', 'Real persuasion happens when every question a piece needs is answered, in the same person, at the same time, and the answers agree with each other. ' +
|
|
49
50
|
'When one answer contradicts another, the reader feels that something is off, and they discount everything.');
|
package/dist/tools/catalog.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { ROLES, roleLabel } from '../roles.js';
|
|
3
3
|
import { searchTechniques, lookupTechnique, resolveCategory, TECHNIQUES } from '../data.js';
|
|
4
4
|
import { formatSchema, categorySchema, roleSchema } from '../schemas.js';
|
|
5
|
-
import { text, errorText, techniqueLine, usageSentence, exampleSource } from '../format.js';
|
|
5
|
+
import { text, errorText, techniqueLine, usageSentence, exampleSource, tracked } from '../format.js';
|
|
6
6
|
import { joinList, clipText, innerQuotes } from '../voice.js';
|
|
7
7
|
export function registerCatalogTools(server) {
|
|
8
8
|
server.registerTool('find_persuasion_techniques', {
|
|
@@ -55,7 +55,7 @@ export function registerCatalogTools(server) {
|
|
|
55
55
|
const cat = resolveCategory(category);
|
|
56
56
|
const out = [];
|
|
57
57
|
out.push(`# ${t.name} (${t.pt_id})`);
|
|
58
|
-
out.push(`It answers ${roleLabel(t.role)}, which is about ${ROLES[t.role].gloss}. ${usageSentence(t, cat)} ${t.url}`);
|
|
58
|
+
out.push(`It answers ${roleLabel(t.role)}, which is about ${ROLES[t.role].gloss}. ${usageSentence(t, cat)} ${tracked(t.url)}`);
|
|
59
59
|
out.push('');
|
|
60
60
|
// Catalog fields: whole sentences only (the export cuts long fields), and condition lists read as sentences.
|
|
61
61
|
const prose = (s) => clipText(s.replace(/;\s+(?=[A-Z])/g, '. '), 4000);
|
package/dist/tools/plan.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { ROLE_NAMES, ROLES, GOAL_REQUIREMENTS, GOAL_NOUN, GOAL_CLAUSE, FORMAT_LABEL, AWARENESS_WEIGHT, COHERENCE_CHECKS, expectedRoles, roleLabel } from '../roles.js';
|
|
3
3
|
import { pickMoves, resolveCategory } from '../data.js';
|
|
4
4
|
import { goalSchema, formatSchema, awarenessSchema, categorySchema, modeSchema } from '../schemas.js';
|
|
5
|
-
import { text, techniqueLine } from '../format.js';
|
|
5
|
+
import { text, techniqueLine, tracked } from '../format.js';
|
|
6
6
|
import { cap, joinList } from '../voice.js';
|
|
7
7
|
const NUM = ['no', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
|
|
8
8
|
export function registerPlanTool(server) {
|
|
@@ -78,7 +78,7 @@ export function registerPlanTool(server) {
|
|
|
78
78
|
lines.push(`${cap(GOAL_NOUN[goal])} doesn't strictly need these, but one well-placed line can still help.`);
|
|
79
79
|
for (const role of optional) {
|
|
80
80
|
const [p] = pickMoves(role, { n: 1, format, category: cat, seed, context, mode, exclude: used });
|
|
81
|
-
lines.push(`- ${roleLabel(role)}${p ? ` One way in is **${p.t.name}** (${p.t.pt_id}). ${p.t.url}` : ''}`);
|
|
81
|
+
lines.push(`- ${roleLabel(role)}${p ? ` One way in is **${p.t.name}** (${p.t.pt_id}). ${tracked(p.t.url)}` : ''}`);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
const checks = COHERENCE_CHECKS.filter((c) => expected.has(c.roles[0]) && expected.has(c.roles[1])).slice(0, 4);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coppica/persuasion-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A free MCP server that helps any AI write and check marketing copy: the nine questions every reader asks, 766 persuasion techniques from real ads, copy diagnostics and A/B test math. No API key, no AI calls.",
|
|
5
5
|
"mcpName": "com.coppica/persuasion-taxonomy",
|
|
6
6
|
"type": "module",
|
package/data/trait-evidence.json
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"source": "Upworthy Research Archive (Matias, Munger, Le Quere, Ebersole 2021, Nature Scientific Data), CC BY 4.0",
|
|
3
|
-
"method": "Within-test trait analysis on the Upworthy Research Archive (CC BY 4.0, Matias et al. 2021).\nUnit = one test x one image (eyecatcher), so only headlines shown with the same image are compared.\nPackages need >= MIN_IMP impressions. For a binary trait, a unit votes 1 if its packages WITH the\ntrait have a higher pooled CTR than those without. For a continuous trait, the unit compares its\nhighest- and lowest-valued packages. Win rate > 50% = the trait tends to win. Tests in the\nbroken-randomization window (2013-06-25 .. 2014-01-10) are excluded.",
|
|
4
|
-
"min_impressions": 1000,
|
|
5
|
-
"traits": {
|
|
6
|
-
"number": {
|
|
7
|
-
"confirmatory_win_rate": 0.5443,
|
|
8
|
-
"confirmatory_n": 3316,
|
|
9
|
-
"ci95": [
|
|
10
|
-
0.5273,
|
|
11
|
-
0.5612
|
|
12
|
-
],
|
|
13
|
-
"holdout_win_rate": 0.5811,
|
|
14
|
-
"holdout_n": 721,
|
|
15
|
-
"exploratory_win_rate": 0.5405,
|
|
16
|
-
"verdict": "REPLICATES wins"
|
|
17
|
-
},
|
|
18
|
-
"question": {
|
|
19
|
-
"confirmatory_win_rate": 0.3856,
|
|
20
|
-
"confirmatory_n": 4217,
|
|
21
|
-
"ci95": [
|
|
22
|
-
0.371,
|
|
23
|
-
0.4004
|
|
24
|
-
],
|
|
25
|
-
"holdout_win_rate": 0.3619,
|
|
26
|
-
"holdout_n": 923,
|
|
27
|
-
"exploratory_win_rate": 0.3838,
|
|
28
|
-
"verdict": "REPLICATES loses"
|
|
29
|
-
},
|
|
30
|
-
"you": {
|
|
31
|
-
"confirmatory_win_rate": 0.487,
|
|
32
|
-
"confirmatory_n": 5010,
|
|
33
|
-
"ci95": [
|
|
34
|
-
0.4732,
|
|
35
|
-
0.5009
|
|
36
|
-
],
|
|
37
|
-
"holdout_win_rate": 0.5032,
|
|
38
|
-
"holdout_n": 1099,
|
|
39
|
-
"exploratory_win_rate": 0.4876,
|
|
40
|
-
"verdict": "no reliable effect"
|
|
41
|
-
},
|
|
42
|
-
"first_person": {
|
|
43
|
-
"confirmatory_win_rate": 0.5001,
|
|
44
|
-
"confirmatory_n": 4475,
|
|
45
|
-
"ci95": [
|
|
46
|
-
0.4855,
|
|
47
|
-
0.5148
|
|
48
|
-
],
|
|
49
|
-
"holdout_win_rate": 0.4964,
|
|
50
|
-
"holdout_n": 985,
|
|
51
|
-
"exploratory_win_rate": 0.4743,
|
|
52
|
-
"verdict": "no reliable effect"
|
|
53
|
-
},
|
|
54
|
-
"quotation": {
|
|
55
|
-
"confirmatory_win_rate": 0.4377,
|
|
56
|
-
"confirmatory_n": 361,
|
|
57
|
-
"ci95": [
|
|
58
|
-
0.3874,
|
|
59
|
-
0.4892
|
|
60
|
-
],
|
|
61
|
-
"holdout_win_rate": 0.4177,
|
|
62
|
-
"holdout_n": 79,
|
|
63
|
-
"exploratory_win_rate": 0.4754,
|
|
64
|
-
"verdict": "no reliable effect"
|
|
65
|
-
},
|
|
66
|
-
"negation": {
|
|
67
|
-
"confirmatory_win_rate": 0.4996,
|
|
68
|
-
"confirmatory_n": 4548,
|
|
69
|
-
"ci95": [
|
|
70
|
-
0.485,
|
|
71
|
-
0.5141
|
|
72
|
-
],
|
|
73
|
-
"holdout_win_rate": 0.5165,
|
|
74
|
-
"holdout_n": 999,
|
|
75
|
-
"exploratory_win_rate": 0.4839,
|
|
76
|
-
"verdict": "no reliable effect"
|
|
77
|
-
},
|
|
78
|
-
"superlative": {
|
|
79
|
-
"confirmatory_win_rate": 0.5334,
|
|
80
|
-
"confirmatory_n": 2261,
|
|
81
|
-
"ci95": [
|
|
82
|
-
0.5128,
|
|
83
|
-
0.5539
|
|
84
|
-
],
|
|
85
|
-
"holdout_win_rate": 0.5382,
|
|
86
|
-
"holdout_n": 524,
|
|
87
|
-
"exploratory_win_rate": 0.5558,
|
|
88
|
-
"verdict": "no reliable effect"
|
|
89
|
-
},
|
|
90
|
-
"this_these": {
|
|
91
|
-
"confirmatory_win_rate": 0.5578,
|
|
92
|
-
"confirmatory_n": 5522,
|
|
93
|
-
"ci95": [
|
|
94
|
-
0.5446,
|
|
95
|
-
0.5708
|
|
96
|
-
],
|
|
97
|
-
"holdout_win_rate": 0.5725,
|
|
98
|
-
"holdout_n": 1214,
|
|
99
|
-
"exploratory_win_rate": 0.5526,
|
|
100
|
-
"verdict": "REPLICATES wins"
|
|
101
|
-
},
|
|
102
|
-
"exclamation": {
|
|
103
|
-
"confirmatory_win_rate": 0.4034,
|
|
104
|
-
"confirmatory_n": 761,
|
|
105
|
-
"ci95": [
|
|
106
|
-
0.3691,
|
|
107
|
-
0.4387
|
|
108
|
-
],
|
|
109
|
-
"holdout_win_rate": 0.3701,
|
|
110
|
-
"holdout_n": 154,
|
|
111
|
-
"exploratory_win_rate": 0.3576,
|
|
112
|
-
"verdict": "REPLICATES loses"
|
|
113
|
-
},
|
|
114
|
-
"emphasis_caps": {
|
|
115
|
-
"confirmatory_win_rate": 0.4178,
|
|
116
|
-
"confirmatory_n": 1776,
|
|
117
|
-
"ci95": [
|
|
118
|
-
0.3951,
|
|
119
|
-
0.4409
|
|
120
|
-
],
|
|
121
|
-
"holdout_win_rate": 0.4474,
|
|
122
|
-
"holdout_n": 380,
|
|
123
|
-
"exploratory_win_rate": 0.4103,
|
|
124
|
-
"verdict": "REPLICATES loses"
|
|
125
|
-
},
|
|
126
|
-
"how_why_what": {
|
|
127
|
-
"confirmatory_win_rate": 0.4963,
|
|
128
|
-
"confirmatory_n": 5561,
|
|
129
|
-
"ci95": [
|
|
130
|
-
0.4832,
|
|
131
|
-
0.5095
|
|
132
|
-
],
|
|
133
|
-
"holdout_win_rate": 0.5033,
|
|
134
|
-
"holdout_n": 1220,
|
|
135
|
-
"exploratory_win_rate": 0.5042,
|
|
136
|
-
"verdict": "no reliable effect"
|
|
137
|
-
},
|
|
138
|
-
"time_word": {
|
|
139
|
-
"confirmatory_win_rate": 0.5366,
|
|
140
|
-
"confirmatory_n": 2158,
|
|
141
|
-
"ci95": [
|
|
142
|
-
0.5155,
|
|
143
|
-
0.5576
|
|
144
|
-
],
|
|
145
|
-
"holdout_win_rate": 0.5593,
|
|
146
|
-
"holdout_n": 447,
|
|
147
|
-
"exploratory_win_rate": 0.5441,
|
|
148
|
-
"verdict": "REPLICATES wins"
|
|
149
|
-
},
|
|
150
|
-
"old_scoreHook": {
|
|
151
|
-
"confirmatory_win_rate": 0.4632,
|
|
152
|
-
"confirmatory_n": 7726,
|
|
153
|
-
"ci95": [
|
|
154
|
-
0.4521,
|
|
155
|
-
0.4744
|
|
156
|
-
],
|
|
157
|
-
"holdout_win_rate": 0.4949,
|
|
158
|
-
"holdout_n": 1673,
|
|
159
|
-
"exploratory_win_rate": 0.482,
|
|
160
|
-
"verdict": "no reliable effect"
|
|
161
|
-
},
|
|
162
|
-
"word_count": {
|
|
163
|
-
"confirmatory_win_rate": 0.5385,
|
|
164
|
-
"confirmatory_n": 9564,
|
|
165
|
-
"ci95": [
|
|
166
|
-
0.5285,
|
|
167
|
-
0.5485
|
|
168
|
-
],
|
|
169
|
-
"holdout_win_rate": 0.5435,
|
|
170
|
-
"holdout_n": 2079,
|
|
171
|
-
"exploratory_win_rate": 0.5493,
|
|
172
|
-
"verdict": "REPLICATES wins"
|
|
173
|
-
},
|
|
174
|
-
"article_overlap": {
|
|
175
|
-
"confirmatory_win_rate": 0.5268,
|
|
176
|
-
"confirmatory_n": 9406,
|
|
177
|
-
"ci95": [
|
|
178
|
-
0.5167,
|
|
179
|
-
0.5369
|
|
180
|
-
],
|
|
181
|
-
"holdout_win_rate": 0.5164,
|
|
182
|
-
"holdout_n": 2043,
|
|
183
|
-
"exploratory_win_rate": 0.5188,
|
|
184
|
-
"verdict": "no reliable effect"
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|