raven-mcp 1.2.0 → 1.3.1
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 +41 -11
- package/dist/index.js +377 -0
- package/dist/index.js.map +1 -1
- package/package.json +12 -3
- package/scripts/postinstall.cjs +8 -0
- package/src/data/brand/principles/brand-as-system.json +106 -0
- package/src/data/brand/principles/gradient-usage.json +90 -0
- package/src/data/brand/principles/imagery.json +70 -0
- package/src/data/brand/principles/logo-usage.json +113 -0
- package/src/data/brand/principles/system-thinking.json +90 -0
- package/src/data/brand/principles/visual-hierarchy.json +68 -0
- package/src/data/brand/trends/2026-current-trends.json +139 -0
- package/src/data/research/frameworks/metrics-frameworks.json +229 -0
- package/src/data/research/methods/qualitative.json +112 -0
- package/src/data/research/methods/quantitative.json +118 -0
- package/src/data/research/methods/usability.json +127 -0
- package/src/data/research/principles/research-fundamentals.json +238 -0
- package/src/data/service-design/frameworks/gov-uk-service-standard.json +179 -0
- package/src/data/service-design/patterns/human-handoff.json +108 -0
- package/src/data/service-design/patterns/moments-of-truth.json +112 -0
- package/src/data/service-design/patterns/omnichannel-continuity.json +104 -0
- package/src/data/service-design/patterns/service-blueprinting.json +107 -0
- package/src/data/service-design/patterns/signup-as-service.json +111 -0
- package/src/data/service-design/principles/service-design-principles.json +216 -0
package/README.md
CHANGED
|
@@ -2,15 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
**Odin's ravens brought back knowledge of the world — Raven brings back design intelligence.**
|
|
4
4
|
|
|
5
|
-
A design knowledge MCP server that Claude can query when generating UI.
|
|
5
|
+
A design knowledge MCP server that Claude can query when generating UI. Eight layers: principles, patterns, content design systems, research methods, service design, brand/visual, business strategy, and design tokens.
|
|
6
6
|
|
|
7
7
|
## What it does
|
|
8
8
|
|
|
9
9
|
Raven gives Claude access to a comprehensive design knowledge base:
|
|
10
10
|
|
|
11
|
-
- **Principles** — Nielsen's 10 Heuristics, all 21 Laws of UX, Gestalt principles, WCAG accessibility, typography rules, color theory, mobile UX, D4D framework,
|
|
12
|
-
- **Patterns** — Proven UI patterns for signup flows, pricing pages, navigation, forms, landing pages, dashboards, modals, empty/error/loading states, CTAs, social proof, mobile conversion — plus content patterns
|
|
11
|
+
- **Principles** — Nielsen's 10 Heuristics, all 21 Laws of UX, Gestalt principles, WCAG accessibility, typography rules, color theory, mobile UX, D4D framework, UX writing, service design, and brand
|
|
12
|
+
- **Patterns** — Proven UI patterns for signup flows, pricing pages, navigation, forms, landing pages, dashboards, modals, empty/error/loading states, CTAs, social proof, mobile conversion — plus content patterns (error messages, empty-state copy, notifications, form validation) and service patterns (service blueprinting, human handoff, signup-as-service, omnichannel continuity, moments of truth)
|
|
13
13
|
- **Content systems** — Voice & tone guides from real brands: Mailchimp, GOV.UK, Shopify Polaris, Atlassian, and Intuit
|
|
14
|
+
- **Research** — Qualitative, quantitative, and usability methods with do/don't protocols and checklists. Metrics frameworks: HEART, AARRR/Pirate, North Star Metric, conversion funnel, RICE, OKRs.
|
|
15
|
+
- **Service design** — Service blueprinting (with HTML blueprint generation — current vs. ideal state), human-handoff patterns, signup-as-service, omnichannel continuity, moments of truth / recovery, and the GOV.UK Service Standard
|
|
16
|
+
- **Brand & visual** — Logo usage (clear space, min sizes, variants, placement, restraint), gradient usage (hierarchy, palette, contrast, trend vs signature), imagery (consistency, representation, purpose), visual hierarchy, brand-as-system, and current (2026) visual-design trends
|
|
14
17
|
- **Business** — Monetization models, retention strategies, onboarding optimization, growth mechanics, and product metrics frameworks
|
|
15
18
|
- **Tokens** — Design system tokens for Stripe, Linear, and more
|
|
16
19
|
|
|
@@ -64,8 +67,24 @@ cd raven-mcp && npm install && npm run build
|
|
|
64
67
|
| `get_content_system` | Get a brand's voice attributes, tone shifts, vocabulary, grammar, and content patterns |
|
|
65
68
|
| `get_content_principles` | Get UX-writing principles — clarity, active voice, error anatomy, inclusive language |
|
|
66
69
|
| `get_content_pattern` | Get copy recipes for error messages, empty-state copy, notifications, form validation |
|
|
70
|
+
| `get_research_method` | Get qualitative, quantitative, or usability research methods with protocols and checklists |
|
|
71
|
+
| `get_metrics_framework` | Get a product-metrics framework — HEART, AARRR, North Star, conversion funnel, RICE, OKRs |
|
|
72
|
+
| `get_service_pattern` | Get a service design pattern — blueprinting, human handoff, signup-as-service, omnichannel, moments of truth |
|
|
73
|
+
| `get_service_standard` | Get the GOV.UK Service Standard — 14 points for evaluating service quality |
|
|
74
|
+
| `generate_service_blueprint` | Render a service blueprint as HTML — current state, or current vs. ideal side-by-side |
|
|
75
|
+
| `get_brand_principles` | Get brand/visual principles — logo, gradient, imagery, hierarchy, brand-as-system |
|
|
76
|
+
| `get_brand_trends` | Get current (2026) brand and visual-design trends with usage guidance |
|
|
67
77
|
| `raven_reflect` | Summarize your local Raven usage log to find patterns + gaps |
|
|
68
78
|
|
|
79
|
+
## Release updates
|
|
80
|
+
|
|
81
|
+
Raven ships new principles, patterns, and brand systems regularly. For one email per minor/major release (patches stay quiet):
|
|
82
|
+
|
|
83
|
+
- **Web:** [ravenmcp.ai/#updates](https://ravenmcp.ai/#updates) — 10 seconds, one email field.
|
|
84
|
+
- **In-product:** ask Claude *"register me for Raven updates at you@work.com"* — Claude calls `raven_register` and you're in.
|
|
85
|
+
|
|
86
|
+
No marketing, unsubscribe anytime. Powered by Resend.
|
|
87
|
+
|
|
69
88
|
## Learning loop
|
|
70
89
|
|
|
71
90
|
Raven keeps a small **local-only** log of how you use it so you (and Claude) can spot which patterns you build most often and which gaps show up again and again.
|
|
@@ -92,12 +111,23 @@ All knowledge lives in `src/data/` as static JSON files:
|
|
|
92
111
|
|
|
93
112
|
```
|
|
94
113
|
src/data/
|
|
95
|
-
principles/
|
|
96
|
-
patterns/
|
|
97
|
-
business/
|
|
98
|
-
tokens/
|
|
99
|
-
content/
|
|
100
|
-
systems/
|
|
101
|
-
principles/
|
|
102
|
-
patterns/
|
|
114
|
+
principles/ # Nielsen, Laws of UX, Gestalt, accessibility, typography, color, mobile, D4D
|
|
115
|
+
patterns/ # signup, pricing, nav, forms, landing, dashboard, modals, empty/error/loading, CTA, social proof, mobile
|
|
116
|
+
business/ # monetization, retention, onboarding, growth, metrics
|
|
117
|
+
tokens/ # registry.json + systems/ (stripe, linear, vercel, …)
|
|
118
|
+
content/ # voice & tone: Mailchimp, GOV.UK, Shopify Polaris, Atlassian, Intuit
|
|
119
|
+
systems/ # registry.json + brand-voice JSONs
|
|
120
|
+
principles/ # UX-writing principles (clarity, active voice, error anatomy, …)
|
|
121
|
+
patterns/ # copy recipes for errors, empty states, notifications, form validation
|
|
122
|
+
research/ # study protocols + metrics frameworks
|
|
123
|
+
principles/ # research fundamentals (method match, bias, sample size, ethics, triangulation, …)
|
|
124
|
+
methods/ # qualitative, quantitative, usability
|
|
125
|
+
frameworks/ # HEART, AARRR, North Star, conversion funnel, RICE, OKRs
|
|
126
|
+
service-design/ # service-level principles + patterns + frameworks
|
|
127
|
+
principles/ # Stickdorn, Shostack, peak-end, moments of truth, handoff
|
|
128
|
+
patterns/ # service blueprinting, human handoff, signup-as-service, omnichannel, moments of truth
|
|
129
|
+
frameworks/ # GOV.UK Service Standard (14 points)
|
|
130
|
+
brand/ # brand & visual design
|
|
131
|
+
principles/ # logo, gradient, imagery, hierarchy, brand-as-system
|
|
132
|
+
trends/ # 2026-current.json
|
|
103
133
|
```
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,17 @@ var CONTENT_DIR = join(DATA_DIR, "content");
|
|
|
18
18
|
var CONTENT_SYSTEMS_DIR = join(CONTENT_DIR, "systems");
|
|
19
19
|
var CONTENT_PRINCIPLES_DIR = join(CONTENT_DIR, "principles");
|
|
20
20
|
var CONTENT_PATTERNS_DIR = join(CONTENT_DIR, "patterns");
|
|
21
|
+
var RESEARCH_DIR = join(DATA_DIR, "research");
|
|
22
|
+
var RESEARCH_PRINCIPLES_DIR = join(RESEARCH_DIR, "principles");
|
|
23
|
+
var RESEARCH_METHODS_DIR = join(RESEARCH_DIR, "methods");
|
|
24
|
+
var RESEARCH_FRAMEWORKS_DIR = join(RESEARCH_DIR, "frameworks");
|
|
25
|
+
var SERVICE_DIR = join(DATA_DIR, "service-design");
|
|
26
|
+
var SERVICE_PRINCIPLES_DIR = join(SERVICE_DIR, "principles");
|
|
27
|
+
var SERVICE_PATTERNS_DIR = join(SERVICE_DIR, "patterns");
|
|
28
|
+
var SERVICE_FRAMEWORKS_DIR = join(SERVICE_DIR, "frameworks");
|
|
29
|
+
var BRAND_DIR = join(DATA_DIR, "brand");
|
|
30
|
+
var BRAND_PRINCIPLES_DIR = join(BRAND_DIR, "principles");
|
|
31
|
+
var BRAND_TRENDS_DIR = join(BRAND_DIR, "trends");
|
|
21
32
|
// ── Data loading ────────────────────────────────────────────────────
|
|
22
33
|
function loadJsonDir(dir) {
|
|
23
34
|
if (!existsSync(dir))
|
|
@@ -50,6 +61,13 @@ function loadAllData() {
|
|
|
50
61
|
// accessed via list_content_systems / get_content_system.
|
|
51
62
|
allPrinciples = allPrinciples.concat(loadJsonDir(CONTENT_PRINCIPLES_DIR));
|
|
52
63
|
allPatterns = allPatterns.concat(loadJsonDir(CONTENT_PATTERNS_DIR));
|
|
64
|
+
// Research / service-design / brand layers — principles follow the same
|
|
65
|
+
// schema and merge into allPrinciples. Service-design also contributes
|
|
66
|
+
// patterns. Research methods, research metrics frameworks, service
|
|
67
|
+
// frameworks, and brand trends use domain-specific shapes and are
|
|
68
|
+
// accessed through dedicated tools.
|
|
69
|
+
allPrinciples = allPrinciples.concat(loadJsonDir(RESEARCH_PRINCIPLES_DIR), loadJsonDir(SERVICE_PRINCIPLES_DIR), loadJsonDir(BRAND_PRINCIPLES_DIR));
|
|
70
|
+
allPatterns = allPatterns.concat(loadJsonDir(SERVICE_PATTERNS_DIR));
|
|
53
71
|
}
|
|
54
72
|
loadAllData();
|
|
55
73
|
// ── Token helpers (from reference) ──────────────────────────────────
|
|
@@ -89,6 +107,10 @@ function getAvailableContentSystemIds() {
|
|
|
89
107
|
.filter(f => f.endsWith(".json") && f !== "registry.json")
|
|
90
108
|
.map(f => f.replace(".json", ""));
|
|
91
109
|
}
|
|
110
|
+
function loadResearchMethods() { return loadJsonDir(RESEARCH_METHODS_DIR); }
|
|
111
|
+
function loadMetricsFrameworks() { return loadJsonDir(RESEARCH_FRAMEWORKS_DIR); }
|
|
112
|
+
function loadServiceFrameworks() { return loadJsonDir(SERVICE_FRAMEWORKS_DIR); }
|
|
113
|
+
function loadBrandTrends() { return loadJsonDir(BRAND_TRENDS_DIR); }
|
|
92
114
|
function flattenTokens(obj, prefix) {
|
|
93
115
|
var results = [];
|
|
94
116
|
var parentType = obj["$type"];
|
|
@@ -444,6 +466,194 @@ function formatPrinciple(p, format) {
|
|
|
444
466
|
}
|
|
445
467
|
return p;
|
|
446
468
|
}
|
|
469
|
+
function escapeHtml(s) {
|
|
470
|
+
if (!s)
|
|
471
|
+
return "";
|
|
472
|
+
return s
|
|
473
|
+
.replace(/&/g, "&")
|
|
474
|
+
.replace(/</g, "<")
|
|
475
|
+
.replace(/>/g, ">")
|
|
476
|
+
.replace(/"/g, """)
|
|
477
|
+
.replace(/'/g, "'");
|
|
478
|
+
}
|
|
479
|
+
function renderBlueprintSection(bp) {
|
|
480
|
+
var stepCount = bp.steps.length;
|
|
481
|
+
var twoActor = !!(bp.actors && bp.actors.b);
|
|
482
|
+
var labelA = (bp.actors && bp.actors.a && bp.actors.a.label) || "User";
|
|
483
|
+
var labelB = twoActor ? bp.actors.b.label : "";
|
|
484
|
+
var cols = bp.steps.map(function (s) {
|
|
485
|
+
return '<div class="step-header">' + escapeHtml(s.label) + '</div>';
|
|
486
|
+
}).join("");
|
|
487
|
+
function rowFromCells(title, accent, cells) {
|
|
488
|
+
return ('<div class="row">' +
|
|
489
|
+
'<div class="row-label ' + accent + '">' + title + '</div>' +
|
|
490
|
+
'<div class="row-cells" style="grid-template-columns: repeat(' + stepCount + ', minmax(0, 1fr));">' + cells + '</div>' +
|
|
491
|
+
'</div>');
|
|
492
|
+
}
|
|
493
|
+
function row(title, accent, field) {
|
|
494
|
+
var cells = bp.steps.map(function (s) {
|
|
495
|
+
var content = s[field];
|
|
496
|
+
return content
|
|
497
|
+
? '<div class="cell">' + escapeHtml(content) + '</div>'
|
|
498
|
+
: '<div class="cell cell-empty"></div>';
|
|
499
|
+
}).join("");
|
|
500
|
+
return rowFromCells(title, accent, cells);
|
|
501
|
+
}
|
|
502
|
+
function actorBRow(title, accent, field) {
|
|
503
|
+
var cells = bp.steps.map(function (s) {
|
|
504
|
+
var content = s.actor_b ? s.actor_b[field] : undefined;
|
|
505
|
+
return content
|
|
506
|
+
? '<div class="cell">' + escapeHtml(content) + '</div>'
|
|
507
|
+
: '<div class="cell cell-empty"></div>';
|
|
508
|
+
}).join("");
|
|
509
|
+
return rowFromCells(title, accent, cells);
|
|
510
|
+
}
|
|
511
|
+
// Pain/delight row is merged — one cell per step
|
|
512
|
+
var pdCells = bp.steps.map(function (s) {
|
|
513
|
+
if (s.pain_point)
|
|
514
|
+
return '<div class="cell pain"><span class="tag tag-pain">Pain</span>' + escapeHtml(s.pain_point) + '</div>';
|
|
515
|
+
if (s.delight)
|
|
516
|
+
return '<div class="cell delight"><span class="tag tag-delight">Moment</span>' + escapeHtml(s.delight) + '</div>';
|
|
517
|
+
return '<div class="cell cell-empty"></div>';
|
|
518
|
+
}).join("");
|
|
519
|
+
var body;
|
|
520
|
+
if (twoActor) {
|
|
521
|
+
// Simple person silhouette avatars — Actor A plain, Actor B with a
|
|
522
|
+
// subtle collar/V to distinguish. Both tint to their lane color.
|
|
523
|
+
// Distinct role icons: User = a person silhouette; Expert = a
|
|
524
|
+
// briefcase (universal shorthand for professional work, reads
|
|
525
|
+
// across law / medicine / finance / real estate / advising).
|
|
526
|
+
var avatarA = '<span class="lane-avatar-wrap">' +
|
|
527
|
+
'<svg class="lane-avatar" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">' +
|
|
528
|
+
'<circle cx="12" cy="9" r="4"/>' +
|
|
529
|
+
'<path d="M4 21c0-4 3.6-6.5 8-6.5s8 2.5 8 6.5v1H4z"/>' +
|
|
530
|
+
'</svg>' +
|
|
531
|
+
'</span>';
|
|
532
|
+
var avatarB = '<span class="lane-avatar-wrap">' +
|
|
533
|
+
'<svg class="lane-avatar" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">' +
|
|
534
|
+
'<path d="M9 5h6a1 1 0 0 1 1 1v2h-8V6a1 1 0 0 1 1-1zm-5 5h16a1 1 0 0 1 1 1v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a1 1 0 0 1 1-1zm8 3a1 1 0 0 0-1 1v1h2v-1a1 1 0 0 0-1-1z"/>' +
|
|
535
|
+
'</svg>' +
|
|
536
|
+
'</span>';
|
|
537
|
+
// Two-actor / HI-loop layout:
|
|
538
|
+
// Actor A (above line of interaction), Actor B (below), then line of visibility, backstage, support.
|
|
539
|
+
body =
|
|
540
|
+
'<div class="swim-lane lane-a">' +
|
|
541
|
+
'<div class="lane-label lane-label-a">' + avatarA + '<span>' + escapeHtml(labelA) + '</span></div>' +
|
|
542
|
+
row("Physical evidence", "row-evidence", "evidence") +
|
|
543
|
+
row("Actions", "row-user", "user_action") +
|
|
544
|
+
row("Frontstage (sees)", "row-frontstage", "frontstage") +
|
|
545
|
+
'</div>' +
|
|
546
|
+
'<div class="line-of-interaction">Line of interaction</div>' +
|
|
547
|
+
'<div class="swim-lane lane-b">' +
|
|
548
|
+
'<div class="lane-label lane-label-b">' + avatarB + '<span>' + escapeHtml(labelB) + '</span></div>' +
|
|
549
|
+
actorBRow("Physical evidence", "row-evidence", "evidence") +
|
|
550
|
+
actorBRow("Actions", "row-user", "action") +
|
|
551
|
+
actorBRow("Frontstage (sees)", "row-frontstage", "frontstage") +
|
|
552
|
+
'</div>' +
|
|
553
|
+
'<div class="line-of-visibility">Line of visibility</div>' +
|
|
554
|
+
row("Backstage", "row-backstage", "backstage") +
|
|
555
|
+
row("Support processes", "row-support", "support") +
|
|
556
|
+
rowFromCells("Pain / moments", "row-pain", pdCells);
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
body =
|
|
560
|
+
row("Physical evidence", "row-evidence", "evidence") +
|
|
561
|
+
row("User actions", "row-user", "user_action") +
|
|
562
|
+
row("Frontstage", "row-frontstage", "frontstage") +
|
|
563
|
+
'<div class="line-of-visibility">Line of visibility</div>' +
|
|
564
|
+
row("Backstage", "row-backstage", "backstage") +
|
|
565
|
+
row("Support processes", "row-support", "support") +
|
|
566
|
+
rowFromCells("Pain / moments", "row-pain", pdCells);
|
|
567
|
+
}
|
|
568
|
+
return ('<section class="blueprint' + (twoActor ? ' two-actor' : '') + '">' +
|
|
569
|
+
(bp.state_label ? '<h2 class="state-label">' + escapeHtml(bp.state_label) + '</h2>' : '') +
|
|
570
|
+
'<div class="steps-header" style="grid-template-columns: 180px repeat(' + stepCount + ', minmax(0, 1fr));">' +
|
|
571
|
+
'<div class="row-label empty-label">Step</div>' +
|
|
572
|
+
cols +
|
|
573
|
+
'</div>' +
|
|
574
|
+
body +
|
|
575
|
+
'</section>');
|
|
576
|
+
}
|
|
577
|
+
function generateServiceBlueprintHtml(current, ideal) {
|
|
578
|
+
var subtitle = current.subtitle ? '<p class="subtitle">' + escapeHtml(current.subtitle) + '</p>' : '';
|
|
579
|
+
var currentBlueprint = renderBlueprintSection({
|
|
580
|
+
...current,
|
|
581
|
+
state_label: current.state_label || (ideal ? "Current state" : undefined)
|
|
582
|
+
});
|
|
583
|
+
var idealBlueprint = ideal
|
|
584
|
+
? renderBlueprintSection({
|
|
585
|
+
...ideal,
|
|
586
|
+
service_name: current.service_name,
|
|
587
|
+
state_label: ideal.state_label || "Ideal state"
|
|
588
|
+
})
|
|
589
|
+
: "";
|
|
590
|
+
var css = ':root {' +
|
|
591
|
+
'--bg-base:#1a1a22;--bg-surface:#212129;--bg-raised:#2a2a33;--bg-alt:#16161e;' +
|
|
592
|
+
'--border:rgba(255,255,255,0.08);--border-strong:rgba(255,255,255,0.12);' +
|
|
593
|
+
'--text-primary:#F0F0F2;--text-secondary:#9498A0;--text-tertiary:#5C5F68;' +
|
|
594
|
+
'--accent-blue:#00BFFF;--accent-green:#00E676;--accent-orange:#FFAB40;--accent-pink:#FF4081;--accent-purple:#B388FF;' +
|
|
595
|
+
'--font-body:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;' +
|
|
596
|
+
'}' +
|
|
597
|
+
'*{margin:0;padding:0;box-sizing:border-box}' +
|
|
598
|
+
'body{font-family:var(--font-body);background:var(--bg-base);color:var(--text-primary);padding:48px 24px;line-height:1.5;font-size:14px;-webkit-font-smoothing:antialiased}' +
|
|
599
|
+
'.container{max-width:1600px;margin:0 auto}' +
|
|
600
|
+
'header{margin-bottom:48px;padding-bottom:24px;border-bottom:1px solid var(--border)}' +
|
|
601
|
+
'header .eyebrow{font-size:11px;letter-spacing:0.12em;text-transform:uppercase;color:var(--accent-blue);font-weight:700;margin-bottom:8px}' +
|
|
602
|
+
'header h1{font-size:32px;font-weight:800;letter-spacing:-0.02em;margin-bottom:8px}' +
|
|
603
|
+
'header .subtitle{color:var(--text-secondary);font-size:16px;max-width:720px}' +
|
|
604
|
+
'.blueprint{margin-bottom:64px}' +
|
|
605
|
+
'.state-label{font-size:18px;font-weight:700;color:var(--text-primary);margin-bottom:16px;padding-bottom:8px;border-bottom:1px solid var(--border)}' +
|
|
606
|
+
'.steps-header{display:grid;gap:8px;margin-bottom:12px}' +
|
|
607
|
+
'.step-header{background:var(--bg-raised);padding:12px 14px;font-weight:700;font-size:13px;border-radius:10px;border:1px solid var(--border);text-align:center}' +
|
|
608
|
+
'.row{display:grid;grid-template-columns:180px 1fr;gap:8px;margin-bottom:8px;align-items:stretch}' +
|
|
609
|
+
'.row-label{padding:12px 14px;font-size:11px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;border-radius:10px;display:flex;align-items:center;min-height:72px}' +
|
|
610
|
+
'.row-evidence{background:rgba(179,136,255,0.08);color:var(--accent-purple);border:1px solid rgba(179,136,255,0.18)}' +
|
|
611
|
+
'.row-user{background:rgba(0,191,255,0.08);color:var(--accent-blue);border:1px solid rgba(0,191,255,0.18)}' +
|
|
612
|
+
'.row-frontstage{background:rgba(0,230,118,0.08);color:var(--accent-green);border:1px solid rgba(0,230,118,0.18)}' +
|
|
613
|
+
'.row-backstage{background:rgba(255,171,64,0.08);color:var(--accent-orange);border:1px solid rgba(255,171,64,0.18)}' +
|
|
614
|
+
'.row-support{background:rgba(255,64,129,0.06);color:var(--accent-pink);border:1px solid rgba(255,64,129,0.14)}' +
|
|
615
|
+
'.row-pain{background:rgba(255,255,255,0.04);color:var(--text-secondary);border:1px solid var(--border)}' +
|
|
616
|
+
'.swim-lane{padding:18px 18px 14px;position:relative;border-radius:14px;margin-bottom:4px}' +
|
|
617
|
+
'.lane-a{background:rgba(0,191,255,0.04);border:1px solid rgba(0,191,255,0.15);border-left:4px solid var(--accent-blue)}' +
|
|
618
|
+
'.lane-b{background:rgba(255,171,64,0.04);border:1px solid rgba(255,171,64,0.15);border-left:4px solid var(--accent-orange)}' +
|
|
619
|
+
'.lane-label{font-size:11px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--text-tertiary);margin-bottom:14px;display:inline-flex;align-items:center;gap:10px;padding:6px 16px 6px 6px;border-radius:9999px;background:rgba(255,255,255,0.05);border:1px solid var(--border)}' +
|
|
620
|
+
'.lane-avatar-wrap{width:28px;height:28px;border-radius:9999px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}' +
|
|
621
|
+
'.lane-avatar{width:20px;height:20px}' +
|
|
622
|
+
'.lane-label-a .lane-avatar-wrap{background:var(--accent-blue)}' +
|
|
623
|
+
'.lane-label-b .lane-avatar-wrap{background:var(--accent-orange)}' +
|
|
624
|
+
'.lane-label-a .lane-avatar{color:var(--bg-base)}' +
|
|
625
|
+
'.lane-label-b .lane-avatar{color:var(--bg-base)}' +
|
|
626
|
+
'.lane-label-a{color:var(--accent-blue);background:rgba(0,191,255,0.12);border-color:rgba(0,191,255,0.28)}' +
|
|
627
|
+
'.lane-label-b{color:var(--accent-orange);background:rgba(255,171,64,0.12);border-color:rgba(255,171,64,0.28)}' +
|
|
628
|
+
'.line-of-interaction{text-align:center;font-size:11px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--text-primary);padding:14px 0;border-top:2px solid var(--border-strong);border-bottom:2px solid var(--border-strong);margin:10px 0;background:linear-gradient(90deg,rgba(0,191,255,0.10),rgba(255,171,64,0.10))}' +
|
|
629
|
+
'.empty-label{background:transparent;border:1px dashed var(--border);color:var(--text-tertiary)}' +
|
|
630
|
+
'.row-cells{display:grid;gap:8px}' +
|
|
631
|
+
'.cell{background:var(--bg-surface);border:1px solid var(--border);border-radius:10px;padding:12px 14px;font-size:13px;color:var(--text-primary);min-height:72px;display:flex;flex-direction:column;gap:6px}' +
|
|
632
|
+
'.cell.pain{background:rgba(255,64,129,0.08);border-color:rgba(255,64,129,0.22)}' +
|
|
633
|
+
'.cell.delight{background:rgba(0,230,118,0.08);border-color:rgba(0,230,118,0.22)}' +
|
|
634
|
+
'.cell.cell-empty{background:transparent;border:none;padding:0;min-height:72px}' +
|
|
635
|
+
'.tag{display:inline-block;font-size:10px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;padding:2px 8px;border-radius:9999px;width:fit-content}' +
|
|
636
|
+
'.tag-pain{background:rgba(255,64,129,0.18);color:var(--accent-pink)}' +
|
|
637
|
+
'.tag-delight{background:rgba(0,230,118,0.18);color:var(--accent-green)}' +
|
|
638
|
+
'.line-of-visibility{text-align:center;font-size:11px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--text-tertiary);padding:14px 0;border-top:2px dashed var(--border-strong);border-bottom:2px dashed var(--border-strong);margin:14px 0}' +
|
|
639
|
+
'footer{margin-top:48px;padding-top:24px;border-top:1px solid var(--border);color:var(--text-tertiary);font-size:12px;text-align:center}';
|
|
640
|
+
var html = '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">' +
|
|
641
|
+
'<title>Service blueprint — ' + escapeHtml(current.service_name) + '</title>' +
|
|
642
|
+
'<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>' +
|
|
643
|
+
'<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">' +
|
|
644
|
+
'<style>' + css + '</style>' +
|
|
645
|
+
'</head><body><div class="container">' +
|
|
646
|
+
'<header>' +
|
|
647
|
+
'<div class="eyebrow">Service blueprint</div>' +
|
|
648
|
+
'<h1>' + escapeHtml(current.service_name) + '</h1>' +
|
|
649
|
+
subtitle +
|
|
650
|
+
'</header>' +
|
|
651
|
+
currentBlueprint +
|
|
652
|
+
idealBlueprint +
|
|
653
|
+
'<footer>Generated by Raven · frontstage / line of visibility / backstage · Shostack-Stickdorn</footer>' +
|
|
654
|
+
'</div></body></html>';
|
|
655
|
+
return html;
|
|
656
|
+
}
|
|
447
657
|
// ── Update check ────────────────────────────────────────────────────
|
|
448
658
|
// On startup, fetch the latest published version from npm and compare against
|
|
449
659
|
// our own. Minor/major bumps get surfaced via stderr + injected into the first
|
|
@@ -597,6 +807,32 @@ function extractInsight(toolName, input, output) {
|
|
|
597
807
|
case "get_content_pattern":
|
|
598
808
|
insight = { type: input?.type };
|
|
599
809
|
break;
|
|
810
|
+
case "get_research_method":
|
|
811
|
+
insight = { category: input?.category, search: !!input?.search };
|
|
812
|
+
break;
|
|
813
|
+
case "get_metrics_framework":
|
|
814
|
+
insight = { id: input?.id, search: !!input?.search };
|
|
815
|
+
break;
|
|
816
|
+
case "get_service_pattern":
|
|
817
|
+
insight = { type: input?.type };
|
|
818
|
+
break;
|
|
819
|
+
case "get_service_standard":
|
|
820
|
+
insight = { action: "service-standard" };
|
|
821
|
+
break;
|
|
822
|
+
case "generate_service_blueprint":
|
|
823
|
+
insight = {
|
|
824
|
+
has_ideal: Array.isArray(input?.ideal),
|
|
825
|
+
current_steps: Array.isArray(input?.current) ? input.current.length : 0,
|
|
826
|
+
ideal_steps: Array.isArray(input?.ideal) ? input.ideal.length : 0,
|
|
827
|
+
two_actor: !!(input?.actors && input.actors.b)
|
|
828
|
+
};
|
|
829
|
+
break;
|
|
830
|
+
case "get_brand_principles":
|
|
831
|
+
insight = { topic: safeStr(input?.topic, 32), format: input?.format };
|
|
832
|
+
break;
|
|
833
|
+
case "get_brand_trends":
|
|
834
|
+
insight = { action: "trends" };
|
|
835
|
+
break;
|
|
600
836
|
case "generate_design_system":
|
|
601
837
|
insight = { style: input?.style, has_brand_color: !!input?.brand_color, format: input?.format };
|
|
602
838
|
break;
|
|
@@ -2271,6 +2507,147 @@ server.tool("get_content_pattern", "Get content design patterns — copy recipes
|
|
|
2271
2507
|
}]
|
|
2272
2508
|
};
|
|
2273
2509
|
});
|
|
2510
|
+
// ── Research / service-design / brand tools ───────────────────────
|
|
2511
|
+
server.tool("get_research_method", "Get research method details — qualitative (interviews, contextual inquiry, diary, field, intercept), quantitative (surveys, analytics, A/B tests, benchmarking, clickstream), or usability (moderated, unmoderated, 5-second, card sort, tree test, heuristic eval). Returns specific protocols, do/don't guidance, evidence, and a checklist. Use when the user is designing a study or asking how to measure something.", {
|
|
2512
|
+
category: z.enum(["qualitative", "quantitative", "usability", "all"]).optional().describe("Which family of methods. Default: all."),
|
|
2513
|
+
search: z.string().optional().describe("Search within methods by name or description.")
|
|
2514
|
+
}, async ({ category, search }) => {
|
|
2515
|
+
var methods = loadResearchMethods();
|
|
2516
|
+
var result = methods;
|
|
2517
|
+
if (category && category !== "all") {
|
|
2518
|
+
result = methods.filter((m) => m.id && m.id.startsWith(category));
|
|
2519
|
+
}
|
|
2520
|
+
if (search) {
|
|
2521
|
+
var q = search.toLowerCase();
|
|
2522
|
+
result = result.filter((m) => (m.name && m.name.toLowerCase().includes(q)) ||
|
|
2523
|
+
(m.summary && m.summary.toLowerCase().includes(q)) ||
|
|
2524
|
+
(m.patterns && m.patterns.some((p) => (p.name && p.name.toLowerCase().includes(q)) ||
|
|
2525
|
+
(p.description && p.description.toLowerCase().includes(q)))));
|
|
2526
|
+
}
|
|
2527
|
+
return {
|
|
2528
|
+
content: [{
|
|
2529
|
+
type: "text",
|
|
2530
|
+
text: JSON.stringify({ count: result.length, methods: result }, null, 2)
|
|
2531
|
+
}]
|
|
2532
|
+
};
|
|
2533
|
+
});
|
|
2534
|
+
server.tool("get_metrics_framework", "Get a product-metrics framework — HEART (Google), AARRR/Pirate (Dave McClure), North Star Metric, Conversion Funnel, RICE Scoring, or OKRs. Returns structure, when-to-use, pitfalls, and examples. Use when the user asks 'how should we measure success?' or 'what metrics should we track?'", {
|
|
2535
|
+
id: z.string().optional().describe("Framework id (heart, aarrr, north-star-metric, conversion-funnel, rice-scoring, okrs). Omit to list all."),
|
|
2536
|
+
search: z.string().optional().describe("Search for a framework by name or summary.")
|
|
2537
|
+
}, async ({ id, search }) => {
|
|
2538
|
+
var frameworks = loadMetricsFrameworks();
|
|
2539
|
+
var flat = [];
|
|
2540
|
+
for (var file of frameworks) {
|
|
2541
|
+
if (Array.isArray(file?.frameworks))
|
|
2542
|
+
flat = flat.concat(file.frameworks.map((f) => ({ ...f, source_file: file.id })));
|
|
2543
|
+
}
|
|
2544
|
+
if (id) {
|
|
2545
|
+
var match = flat.find(f => f.id === id);
|
|
2546
|
+
if (!match) {
|
|
2547
|
+
return { content: [{ type: "text", text: "Framework '" + id + "' not found. Available: " + flat.map(f => f.id).join(", ") }] };
|
|
2548
|
+
}
|
|
2549
|
+
return { content: [{ type: "text", text: JSON.stringify(match, null, 2) }] };
|
|
2550
|
+
}
|
|
2551
|
+
if (search) {
|
|
2552
|
+
var q = search.toLowerCase();
|
|
2553
|
+
flat = flat.filter(f => (f.name && f.name.toLowerCase().includes(q)) || (f.summary && f.summary.toLowerCase().includes(q)));
|
|
2554
|
+
}
|
|
2555
|
+
return { content: [{ type: "text", text: JSON.stringify({ count: flat.length, frameworks: flat }, null, 2) }] };
|
|
2556
|
+
});
|
|
2557
|
+
server.tool("get_service_pattern", "Get a service design pattern — service blueprinting, human handoff, signup-as-service, omnichannel continuity, or moments of truth / recovery. Returns patterns, do/don't guidance, evidence, and a checklist. Use when the user is designing a service flow, escalation, cross-channel experience, or moment of truth.", {
|
|
2558
|
+
type: z.enum(["service-blueprinting", "human-handoff", "signup-as-service", "omnichannel-continuity", "moments-of-truth"]).describe("Service design pattern type")
|
|
2559
|
+
}, async ({ type }) => {
|
|
2560
|
+
var pattern = allPatterns.find(p => p.id === type && p.category === "service-design");
|
|
2561
|
+
if (!pattern) {
|
|
2562
|
+
return {
|
|
2563
|
+
content: [{
|
|
2564
|
+
type: "text",
|
|
2565
|
+
text: "Service pattern '" + type + "' not found. Available: service-blueprinting, human-handoff, signup-as-service, omnichannel-continuity, moments-of-truth."
|
|
2566
|
+
}]
|
|
2567
|
+
};
|
|
2568
|
+
}
|
|
2569
|
+
return { content: [{ type: "text", text: JSON.stringify(pattern, null, 2) }] };
|
|
2570
|
+
});
|
|
2571
|
+
server.tool("get_service_standard", "Get the GOV.UK Service Standard — 14 points the UK government uses to assess whether a public service is ready to launch. Widely applicable as a rigorous service-quality checklist beyond government. Use when the user asks how to evaluate a whole service.", {}, async () => {
|
|
2572
|
+
var frameworks = loadServiceFrameworks();
|
|
2573
|
+
var govuk = frameworks.find((f) => f.id === "gov-uk-service-standard");
|
|
2574
|
+
if (!govuk) {
|
|
2575
|
+
return { content: [{ type: "text", text: "GOV.UK service standard not found." }] };
|
|
2576
|
+
}
|
|
2577
|
+
return { content: [{ type: "text", text: JSON.stringify(govuk, null, 2) }] };
|
|
2578
|
+
});
|
|
2579
|
+
server.tool("generate_service_blueprint", "Render a service blueprint as a self-contained HTML page. Supports two modes: (1) classic Shostack single-actor blueprint — user action, frontstage, backstage, support, evidence, pain/delight; (2) two-actor HI-loop blueprint — when `actors` is supplied, renders two swim lanes with a line of interaction between them (e.g. customer ↔ lawyer, patient ↔ doctor, buyer ↔ agent). Each actor gets their own actions, frontstage (what they see), and evidence. Optionally accepts an ideal-state to render side-by-side with the current state.", {
|
|
2580
|
+
service_name: z.string().describe("Name of the service (e.g. 'Free trial signup', 'Client intake', 'Restaurant reservation')"),
|
|
2581
|
+
subtitle: z.string().optional().describe("Short description or context line under the title"),
|
|
2582
|
+
actors: z.object({
|
|
2583
|
+
a: z.object({ label: z.string() }).optional().describe("Actor A label (default: 'User'). Use when you want to name the first side (e.g. 'Customer', 'Patient')."),
|
|
2584
|
+
b: z.object({ label: z.string() }).describe("Actor B label (e.g. 'Lawyer', 'Doctor', 'Agent'). Presence of this field switches to two-actor layout.")
|
|
2585
|
+
}).optional().describe("Omit for classic single-actor Shostack blueprint. Provide to render a two-swim-lane HI-loop blueprint with a line of interaction between the two sides."),
|
|
2586
|
+
current: z.array(z.object({
|
|
2587
|
+
label: z.string().describe("Step label (e.g. 'Discover', 'Sign up', 'First use')"),
|
|
2588
|
+
user_action: z.string().optional().describe("In single-actor: what the user does. In two-actor: what actor A does."),
|
|
2589
|
+
frontstage: z.string().optional().describe("In single-actor: what the user SEES (UI, agent greeting). In two-actor: what actor A sees."),
|
|
2590
|
+
backstage: z.string().optional().describe("What neither actor sees directly — shared systems, internal processes, back-office work"),
|
|
2591
|
+
support: z.string().optional().describe("Supporting processes, systems, third-party dependencies"),
|
|
2592
|
+
evidence: z.string().optional().describe("In single-actor: artifact user receives. In two-actor: artifact actor A has."),
|
|
2593
|
+
pain_point: z.string().optional().describe("Known pain point at this step (shown as red callout)"),
|
|
2594
|
+
delight: z.string().optional().describe("Designed moment of delight at this step (shown as green callout)"),
|
|
2595
|
+
actor_b: z.object({
|
|
2596
|
+
action: z.string().optional().describe("What actor B does in this step"),
|
|
2597
|
+
frontstage: z.string().optional().describe("What actor B sees — their own UI, tools, views"),
|
|
2598
|
+
evidence: z.string().optional().describe("Artifact actor B has — case file, notes, record")
|
|
2599
|
+
}).optional().describe("Only used when `actors.b` is provided. Captures the other side of the interaction.")
|
|
2600
|
+
})).describe("The current-state blueprint as an array of steps"),
|
|
2601
|
+
ideal: z.array(z.object({
|
|
2602
|
+
label: z.string(),
|
|
2603
|
+
user_action: z.string().optional(),
|
|
2604
|
+
frontstage: z.string().optional(),
|
|
2605
|
+
backstage: z.string().optional(),
|
|
2606
|
+
support: z.string().optional(),
|
|
2607
|
+
evidence: z.string().optional(),
|
|
2608
|
+
pain_point: z.string().optional(),
|
|
2609
|
+
delight: z.string().optional(),
|
|
2610
|
+
actor_b: z.object({
|
|
2611
|
+
action: z.string().optional(),
|
|
2612
|
+
frontstage: z.string().optional(),
|
|
2613
|
+
evidence: z.string().optional()
|
|
2614
|
+
}).optional()
|
|
2615
|
+
})).optional().describe("Optional ideal-state blueprint — if provided, output shows current AND ideal side-by-side")
|
|
2616
|
+
}, async ({ service_name, subtitle, actors, current, ideal }) => {
|
|
2617
|
+
var html = generateServiceBlueprintHtml({ service_name, subtitle, steps: current, actors: actors }, ideal ? { service_name, steps: ideal, actors: actors } : null);
|
|
2618
|
+
return { content: [{ type: "text", text: html }] };
|
|
2619
|
+
});
|
|
2620
|
+
server.tool("get_brand_principles", "Get brand and visual-design principles — logo usage (clear space, min sizes, variants, placement, restraint), gradient usage (hierarchy, palette, contrast, trend vs signature), imagery (consistency, representation, purpose), visual hierarchy, and brand-as-system thinking. Use when the user asks about branding, logos, gradients, imagery, visual consistency, or how to treat a brand across surfaces.", {
|
|
2621
|
+
topic: z.string().optional().describe("Filter by topic: 'logo', 'gradient', 'imagery', 'hierarchy', 'system', or a freeform search term. Omit to return all brand principles."),
|
|
2622
|
+
format: z.enum(["full", "checklist", "brief"]).optional().describe("Output format. Default: full.")
|
|
2623
|
+
}, async ({ topic, format }) => {
|
|
2624
|
+
var fmt = format || "full";
|
|
2625
|
+
var brand = allPrinciples.filter(p => p.category === "brand");
|
|
2626
|
+
var results = brand;
|
|
2627
|
+
if (topic) {
|
|
2628
|
+
var q = topic.toLowerCase();
|
|
2629
|
+
results = brand.filter(p => {
|
|
2630
|
+
var idMatch = p.id.toLowerCase().includes(q);
|
|
2631
|
+
var nameMatch = p.name.toLowerCase().includes(q);
|
|
2632
|
+
var appliesMatch = p.applies_to ? matchesTags(p.applies_to, topic) : false;
|
|
2633
|
+
var textMatch = textSearch(p.name + " " + p.summary + " " + p.description, topic);
|
|
2634
|
+
return idMatch || nameMatch || appliesMatch || textMatch;
|
|
2635
|
+
});
|
|
2636
|
+
if (results.length === 0)
|
|
2637
|
+
results = brand;
|
|
2638
|
+
}
|
|
2639
|
+
var formatted = results.map(p => formatPrinciple(p, fmt));
|
|
2640
|
+
return {
|
|
2641
|
+
content: [{
|
|
2642
|
+
type: "text",
|
|
2643
|
+
text: JSON.stringify({ topic: topic || "all brand principles", count: formatted.length, principles: formatted }, null, 2)
|
|
2644
|
+
}]
|
|
2645
|
+
};
|
|
2646
|
+
});
|
|
2647
|
+
server.tool("get_brand_trends", "Get current brand and visual-design trends — what's working in 2026 and where each trend fits or fails. Includes bento grids, monospace type, neon-on-dark-glass, generative patterns, brutalism rebound, AI-generated imagery, lowercase/mixed case. Each trend is time-stamped — treat as a calibration signal, not a prescription.", {}, async () => {
|
|
2648
|
+
var trends = loadBrandTrends();
|
|
2649
|
+
return { content: [{ type: "text", text: JSON.stringify({ count: trends.length, trends: trends }, null, 2) }] };
|
|
2650
|
+
});
|
|
2274
2651
|
// ── Reflection ─────────────────────────────────────────────────────
|
|
2275
2652
|
// Summarize the local usage log so the user (or Claude acting on their
|
|
2276
2653
|
// behalf) can see what Raven is being asked to do, and what it's often
|