legends-mcp 1.2.0 → 1.3.0
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/dist/index.js +56 -2
- package/dist/index.js.map +1 -1
- package/dist/legends/loader.d.ts +5 -0
- package/dist/legends/loader.d.ts.map +1 -1
- package/dist/legends/loader.js +96 -12
- package/dist/legends/loader.js.map +1 -1
- package/dist/tools/auto-match.d.ts +49 -0
- package/dist/tools/auto-match.d.ts.map +1 -0
- package/dist/tools/auto-match.js +264 -0
- package/dist/tools/auto-match.js.map +1 -0
- package/dist/tools/index.d.ts +52 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +6 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/party-mode.d.ts +56 -0
- package/dist/tools/party-mode.d.ts.map +1 -0
- package/dist/tools/party-mode.js +226 -0
- package/dist/tools/party-mode.js.map +1 -0
- package/dist/tools/summon-legend.d.ts +3 -0
- package/dist/tools/summon-legend.d.ts.map +1 -1
- package/dist/tools/summon-legend.js +48 -25
- package/dist/tools/summon-legend.js.map +1 -1
- package/dist/utils/sanitize.d.ts +27 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/sanitize.js +84 -0
- package/dist/utils/sanitize.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
7
7
|
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
8
8
|
import { loadLegends, getAllLegends, getLegendById, getLegendCount } from './legends/loader.js';
|
|
9
9
|
import { formatLegendMarkdown } from './legends/prompt-builder.js';
|
|
10
|
-
import { allTools, listLegends, formatLegendsMarkdown, summonLegend, formatSummonedLegend, getLegendContext, getLegendInsight, searchLegends, formatSearchResults, } from './tools/index.js';
|
|
10
|
+
import { allTools, listLegends, formatLegendsMarkdown, summonLegend, formatSummonedLegend, getLegendContext, getLegendInsight, searchLegends, formatSearchResults, partyMode, formatPartyMode, autoMatch, formatAutoMatch, } from './tools/index.js';
|
|
11
11
|
// Load legends at startup
|
|
12
12
|
const legends = loadLegends();
|
|
13
13
|
console.error(`[legends-mcp] Loaded ${getLegendCount()} legends`);
|
|
@@ -15,7 +15,7 @@ console.error(`[legends-mcp] No API key required - Claude does the roleplay!`);
|
|
|
15
15
|
// Create MCP server
|
|
16
16
|
const server = new Server({
|
|
17
17
|
name: 'legends-mcp',
|
|
18
|
-
version: '1.
|
|
18
|
+
version: '1.3.0', // Security release - sync with package.json
|
|
19
19
|
}, {
|
|
20
20
|
capabilities: {
|
|
21
21
|
tools: {},
|
|
@@ -130,6 +130,60 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
130
130
|
content: [{ type: 'text', text: formatted }],
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
|
+
case 'party_mode': {
|
|
134
|
+
const input = args;
|
|
135
|
+
if (!input.question) {
|
|
136
|
+
return {
|
|
137
|
+
content: [{ type: 'text', text: 'Error: question parameter is required' }],
|
|
138
|
+
isError: true,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const result = partyMode(input);
|
|
143
|
+
const formatted = formatPartyMode(result);
|
|
144
|
+
return {
|
|
145
|
+
content: [{ type: 'text', text: formatted }],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
return {
|
|
150
|
+
content: [
|
|
151
|
+
{
|
|
152
|
+
type: 'text',
|
|
153
|
+
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
isError: true,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
case 'auto_match': {
|
|
161
|
+
const input = args;
|
|
162
|
+
if (!input.question) {
|
|
163
|
+
return {
|
|
164
|
+
content: [{ type: 'text', text: 'Error: question parameter is required' }],
|
|
165
|
+
isError: true,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const result = autoMatch(input);
|
|
170
|
+
const formatted = formatAutoMatch(result);
|
|
171
|
+
return {
|
|
172
|
+
content: [{ type: 'text', text: formatted }],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
return {
|
|
177
|
+
content: [
|
|
178
|
+
{
|
|
179
|
+
type: 'text',
|
|
180
|
+
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
isError: true,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
133
187
|
default:
|
|
134
188
|
return {
|
|
135
189
|
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,qBAAqB;AACrB,0DAA0D;AAC1D,kDAAkD;AAElD,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAChG,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EACL,QAAQ,EACR,WAAW,EACX,qBAAqB,EACrB,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/legends/loader.d.ts
CHANGED
|
@@ -17,10 +17,15 @@ export declare function getLegendById(id: string): LegendSkill | undefined;
|
|
|
17
17
|
export declare function getLegendSummaries(): LegendSummary[];
|
|
18
18
|
/**
|
|
19
19
|
* Search legends by query
|
|
20
|
+
* SECURITY: Uses safe string handling to prevent crashes from malformed data
|
|
20
21
|
*/
|
|
21
22
|
export declare function searchLegends(query: string): LegendSkill[];
|
|
22
23
|
/**
|
|
23
24
|
* Get legend count
|
|
24
25
|
*/
|
|
25
26
|
export declare function getLegendCount(): number;
|
|
27
|
+
/**
|
|
28
|
+
* Clear the legends cache (for testing)
|
|
29
|
+
*/
|
|
30
|
+
export declare function clearLegendsCache(): void;
|
|
26
31
|
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/legends/loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/legends/loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAyF9D;;GAEG;AACH,wBAAgB,WAAW,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CA8CtD;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,WAAW,EAAE,CAG7C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAGjE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,aAAa,EAAE,CASpD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAoB1D;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
package/dist/legends/loader.js
CHANGED
|
@@ -3,26 +3,78 @@ import fs from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import yaml from 'yaml';
|
|
6
|
+
import { safeString } from '../utils/sanitize.js';
|
|
6
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
8
|
const __dirname = path.dirname(__filename);
|
|
8
9
|
// Cache for loaded legends
|
|
9
10
|
let legendsCache = null;
|
|
11
|
+
// Maximum file size to prevent DoS (10MB)
|
|
12
|
+
const MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
13
|
+
// Maximum number of legends to load
|
|
14
|
+
const MAX_LEGENDS = 500;
|
|
10
15
|
/**
|
|
11
16
|
* Get the path to the bundled legends directory
|
|
17
|
+
* SECURITY: Only load from package directory unless explicitly allowed
|
|
12
18
|
*/
|
|
13
19
|
function getLegendsDir() {
|
|
14
|
-
//
|
|
20
|
+
// Check for explicit custom directory (opt-in only)
|
|
21
|
+
const customDir = process.env.LEGENDS_MCP_LEGENDS_DIR;
|
|
22
|
+
if (customDir) {
|
|
23
|
+
if (fs.existsSync(customDir)) {
|
|
24
|
+
console.error(`[legends-mcp] Using custom legends directory: ${customDir}`);
|
|
25
|
+
return customDir;
|
|
26
|
+
}
|
|
27
|
+
throw new Error(`Custom legends directory not found: ${customDir}`);
|
|
28
|
+
}
|
|
29
|
+
// SECURITY: Only load from package directory (relative to compiled file)
|
|
30
|
+
// Do NOT fall back to process.cwd() to prevent loading untrusted legends
|
|
15
31
|
const possiblePaths = [
|
|
16
32
|
path.join(__dirname, '..', '..', 'legends'),
|
|
17
33
|
path.join(__dirname, '..', 'legends'),
|
|
18
|
-
path.join(process.cwd(), 'legends'),
|
|
19
34
|
];
|
|
35
|
+
// Only allow CWD if explicitly opted in (for development)
|
|
36
|
+
if (process.env.LEGENDS_MCP_ALLOW_CWD === '1') {
|
|
37
|
+
possiblePaths.push(path.join(process.cwd(), 'legends'));
|
|
38
|
+
console.error('[legends-mcp] WARNING: CWD loading enabled - only use in trusted environments');
|
|
39
|
+
}
|
|
20
40
|
for (const p of possiblePaths) {
|
|
21
41
|
if (fs.existsSync(p)) {
|
|
22
42
|
return p;
|
|
23
43
|
}
|
|
24
44
|
}
|
|
25
|
-
throw new Error('Could not find legends directory. Ensure the package is properly installed.'
|
|
45
|
+
throw new Error('Could not find legends directory. Ensure the package is properly installed. ' +
|
|
46
|
+
'For custom directories, set LEGENDS_MCP_LEGENDS_DIR environment variable.');
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Validate a legend file before loading
|
|
50
|
+
*/
|
|
51
|
+
function validateLegendFile(filePath) {
|
|
52
|
+
try {
|
|
53
|
+
const stats = fs.statSync(filePath);
|
|
54
|
+
if (stats.size > MAX_FILE_SIZE) {
|
|
55
|
+
console.error(`[legends-mcp] Skipping oversized file: ${filePath} (${stats.size} bytes)`);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Validate required fields in a legend
|
|
66
|
+
*/
|
|
67
|
+
function validateLegend(data, filePath) {
|
|
68
|
+
if (!data || typeof data !== 'object') {
|
|
69
|
+
console.error(`[legends-mcp] Invalid legend data in ${filePath}`);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
// Must have at least a name or id
|
|
73
|
+
if (!data.name && !data.id) {
|
|
74
|
+
console.error(`[legends-mcp] Legend missing name/id in ${filePath}`);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
26
78
|
}
|
|
27
79
|
/**
|
|
28
80
|
* Load all legends from YAML files
|
|
@@ -34,24 +86,40 @@ export function loadLegends() {
|
|
|
34
86
|
const legendsDir = getLegendsDir();
|
|
35
87
|
const legends = new Map();
|
|
36
88
|
const entries = fs.readdirSync(legendsDir, { withFileTypes: true });
|
|
89
|
+
let loadedCount = 0;
|
|
37
90
|
for (const entry of entries) {
|
|
38
91
|
if (!entry.isDirectory())
|
|
39
92
|
continue;
|
|
93
|
+
if (loadedCount >= MAX_LEGENDS) {
|
|
94
|
+
console.error(`[legends-mcp] Maximum legends limit reached (${MAX_LEGENDS})`);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
40
97
|
const skillPath = path.join(legendsDir, entry.name, 'skill.yaml');
|
|
41
98
|
if (!fs.existsSync(skillPath))
|
|
42
99
|
continue;
|
|
100
|
+
// Validate file before loading
|
|
101
|
+
if (!validateLegendFile(skillPath))
|
|
102
|
+
continue;
|
|
43
103
|
try {
|
|
44
104
|
const content = fs.readFileSync(skillPath, 'utf-8');
|
|
45
105
|
const data = yaml.parse(content);
|
|
46
|
-
//
|
|
106
|
+
// Validate legend data
|
|
107
|
+
if (!validateLegend(data, skillPath))
|
|
108
|
+
continue;
|
|
109
|
+
// Ensure required fields with safe defaults
|
|
47
110
|
if (!data.id)
|
|
48
111
|
data.id = entry.name;
|
|
49
112
|
if (!data.category)
|
|
50
113
|
data.category = 'legends';
|
|
114
|
+
if (!data.name)
|
|
115
|
+
data.name = entry.name;
|
|
116
|
+
if (!data.description)
|
|
117
|
+
data.description = '';
|
|
51
118
|
legends.set(data.id, data);
|
|
119
|
+
loadedCount++;
|
|
52
120
|
}
|
|
53
121
|
catch (error) {
|
|
54
|
-
console.error(`[
|
|
122
|
+
console.error(`[legends-mcp] Error loading ${skillPath}:`, error);
|
|
55
123
|
}
|
|
56
124
|
}
|
|
57
125
|
legendsCache = legends;
|
|
@@ -78,22 +146,32 @@ export function getLegendSummaries() {
|
|
|
78
146
|
const legends = getAllLegends();
|
|
79
147
|
return legends.map(l => ({
|
|
80
148
|
id: l.id,
|
|
81
|
-
name: l.name,
|
|
82
|
-
description: l.description,
|
|
149
|
+
name: safeString(l.name, l.id),
|
|
150
|
+
description: safeString(l.description, ''),
|
|
83
151
|
expertise: l.owns || [],
|
|
84
152
|
tags: l.tags || [],
|
|
85
153
|
}));
|
|
86
154
|
}
|
|
87
155
|
/**
|
|
88
156
|
* Search legends by query
|
|
157
|
+
* SECURITY: Uses safe string handling to prevent crashes from malformed data
|
|
89
158
|
*/
|
|
90
159
|
export function searchLegends(query) {
|
|
91
160
|
const legends = getAllLegends();
|
|
92
|
-
const q = query.toLowerCase();
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
161
|
+
const q = safeString(query, '').toLowerCase();
|
|
162
|
+
if (!q)
|
|
163
|
+
return [];
|
|
164
|
+
return legends.filter(l => {
|
|
165
|
+
// Safe string comparisons with fallbacks
|
|
166
|
+
const name = safeString(l.name, '').toLowerCase();
|
|
167
|
+
const description = safeString(l.description, '').toLowerCase();
|
|
168
|
+
const tags = (l.tags || []).map(t => safeString(t, '').toLowerCase());
|
|
169
|
+
const owns = (l.owns || []).map(o => safeString(o, '').toLowerCase());
|
|
170
|
+
return (name.includes(q) ||
|
|
171
|
+
description.includes(q) ||
|
|
172
|
+
tags.some(t => t.includes(q)) ||
|
|
173
|
+
owns.some(o => o.includes(q)));
|
|
174
|
+
});
|
|
97
175
|
}
|
|
98
176
|
/**
|
|
99
177
|
* Get legend count
|
|
@@ -101,4 +179,10 @@ export function searchLegends(query) {
|
|
|
101
179
|
export function getLegendCount() {
|
|
102
180
|
return loadLegends().size;
|
|
103
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Clear the legends cache (for testing)
|
|
184
|
+
*/
|
|
185
|
+
export function clearLegendsCache() {
|
|
186
|
+
legendsCache = null;
|
|
187
|
+
}
|
|
104
188
|
//# sourceMappingURL=loader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/legends/loader.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/legends/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface AutoMatchInput {
|
|
2
|
+
question: string;
|
|
3
|
+
max_matches?: number;
|
|
4
|
+
include_prompts?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface MatchedLegend {
|
|
7
|
+
legend_id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
relevance_reason: string;
|
|
10
|
+
expertise_match: string[];
|
|
11
|
+
key_insight: string;
|
|
12
|
+
system_prompt?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface AutoMatchResponse {
|
|
15
|
+
question: string;
|
|
16
|
+
matches: MatchedLegend[];
|
|
17
|
+
suggested_approach: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Auto-match legends to a question
|
|
21
|
+
*/
|
|
22
|
+
export declare function autoMatch(input: AutoMatchInput): AutoMatchResponse;
|
|
23
|
+
/**
|
|
24
|
+
* Format auto-match response for display
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatAutoMatch(response: AutoMatchResponse): string;
|
|
27
|
+
export declare const autoMatchTool: {
|
|
28
|
+
name: string;
|
|
29
|
+
description: string;
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: "object";
|
|
32
|
+
properties: {
|
|
33
|
+
question: {
|
|
34
|
+
type: string;
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
max_matches: {
|
|
38
|
+
type: string;
|
|
39
|
+
description: string;
|
|
40
|
+
};
|
|
41
|
+
include_prompts: {
|
|
42
|
+
type: string;
|
|
43
|
+
description: string;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
required: string[];
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=auto-match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-match.d.ts","sourceRoot":"","sources":["../../src/tools/auto-match.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAmID;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,iBAAiB,CAyClE;AAgBD;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CA8CnE;AAGD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;CA4CzB,CAAC"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// MCP Tool: auto_match
|
|
2
|
+
// Automatically matches and provides context from relevant legends based on user's question
|
|
3
|
+
// This is the "auto proc" feature - proactively giving relevant legend perspectives
|
|
4
|
+
import { getLegendById } from '../legends/loader.js';
|
|
5
|
+
import { buildLegendSystemPrompt } from '../legends/prompt-builder.js';
|
|
6
|
+
import { sanitizeContext, escapeBackticks, safeString } from '../utils/sanitize.js';
|
|
7
|
+
// Keywords to legend expertise mapping
|
|
8
|
+
const EXPERTISE_KEYWORDS = {
|
|
9
|
+
// Crypto & Web3
|
|
10
|
+
crypto: { legends: ['cz-binance', 'vitalik-buterin', 'balaji-srinivasan'], expertise: 'cryptocurrency' },
|
|
11
|
+
bitcoin: { legends: ['michael-saylor', 'jack-dorsey', 'balaji-srinivasan'], expertise: 'Bitcoin' },
|
|
12
|
+
ethereum: { legends: ['vitalik-buterin'], expertise: 'Ethereum' },
|
|
13
|
+
solana: { legends: ['anatoly-yakovenko', 'mert-mumtaz'], expertise: 'Solana' },
|
|
14
|
+
defi: { legends: ['hayden-adams', 'andre-cronje', 'vitalik-buterin'], expertise: 'DeFi' },
|
|
15
|
+
nft: { legends: ['gary-vaynerchuk', 'cz-binance'], expertise: 'NFTs' },
|
|
16
|
+
blockchain: { legends: ['vitalik-buterin', 'balaji-srinivasan', 'cz-binance'], expertise: 'blockchain' },
|
|
17
|
+
web3: { legends: ['balaji-srinivasan', 'chris-dixon'], expertise: 'Web3' },
|
|
18
|
+
exchange: { legends: ['cz-binance', 'brian-armstrong'], expertise: 'crypto exchanges' },
|
|
19
|
+
// Investing
|
|
20
|
+
invest: { legends: ['warren-buffett', 'charlie-munger', 'ray-dalio'], expertise: 'investing' },
|
|
21
|
+
stock: { legends: ['warren-buffett', 'peter-lynch', 'cathie-wood'], expertise: 'stock investing' },
|
|
22
|
+
value: { legends: ['warren-buffett', 'charlie-munger', 'benjamin-graham'], expertise: 'value investing' },
|
|
23
|
+
growth: { legends: ['cathie-wood', 'bill-gurley'], expertise: 'growth investing' },
|
|
24
|
+
portfolio: { legends: ['ray-dalio', 'howard-marks'], expertise: 'portfolio management' },
|
|
25
|
+
risk: { legends: ['ray-dalio', 'howard-marks', 'nassim-taleb'], expertise: 'risk management' },
|
|
26
|
+
venture: { legends: ['marc-andreessen', 'peter-thiel', 'bill-gurley'], expertise: 'venture capital' },
|
|
27
|
+
// Startups
|
|
28
|
+
startup: { legends: ['paul-graham', 'sam-altman', 'peter-thiel'], expertise: 'startups' },
|
|
29
|
+
founder: { legends: ['elon-musk', 'jeff-bezos', 'brian-chesky'], expertise: 'founding companies' },
|
|
30
|
+
fundrais: { legends: ['paul-graham', 'marc-andreessen', 'peter-thiel'], expertise: 'fundraising' },
|
|
31
|
+
pitch: { legends: ['paul-graham', 'guy-kawasaki'], expertise: 'pitching' },
|
|
32
|
+
scale: { legends: ['reid-hoffman', 'patrick-collison', 'tobi-lutke'], expertise: 'scaling' },
|
|
33
|
+
yc: { legends: ['paul-graham', 'sam-altman'], expertise: 'Y Combinator' },
|
|
34
|
+
accelerat: { legends: ['paul-graham', 'sam-altman'], expertise: 'accelerators' },
|
|
35
|
+
// Tech & Product
|
|
36
|
+
product: { legends: ['steve-jobs', 'brian-chesky', 'tobi-lutke'], expertise: 'product development' },
|
|
37
|
+
design: { legends: ['steve-jobs', 'jony-ive', 'brian-chesky'], expertise: 'design' },
|
|
38
|
+
engineer: { legends: ['elon-musk', 'jensen-huang', 'patrick-collison'], expertise: 'engineering' },
|
|
39
|
+
ai: { legends: ['sam-altman', 'demis-hassabis', 'jensen-huang'], expertise: 'artificial intelligence' },
|
|
40
|
+
machine: { legends: ['demis-hassabis', 'andrej-karpathy'], expertise: 'machine learning' },
|
|
41
|
+
gpu: { legends: ['jensen-huang'], expertise: 'GPUs' },
|
|
42
|
+
software: { legends: ['bill-gates', 'satya-nadella', 'marc-andreessen'], expertise: 'software' },
|
|
43
|
+
hardware: { legends: ['steve-jobs', 'jensen-huang', 'elon-musk'], expertise: 'hardware' },
|
|
44
|
+
// Leadership & Management
|
|
45
|
+
lead: { legends: ['satya-nadella', 'jeff-bezos', 'reed-hastings'], expertise: 'leadership' },
|
|
46
|
+
manage: { legends: ['andy-grove', 'ben-horowitz', 'reed-hastings'], expertise: 'management' },
|
|
47
|
+
culture: { legends: ['reed-hastings', 'brian-chesky', 'tobi-lutke'], expertise: 'company culture' },
|
|
48
|
+
team: { legends: ['ben-horowitz', 'patrick-lencioni', 'reed-hastings'], expertise: 'team building' },
|
|
49
|
+
hire: { legends: ['paul-graham', 'ben-horowitz', 'reid-hoffman'], expertise: 'hiring' },
|
|
50
|
+
// Marketing & Growth
|
|
51
|
+
market: { legends: ['seth-godin', 'gary-vaynerchuk'], expertise: 'marketing' },
|
|
52
|
+
brand: { legends: ['gary-vaynerchuk', 'seth-godin', 'steve-jobs'], expertise: 'branding' },
|
|
53
|
+
social: { legends: ['gary-vaynerchuk', 'jack-dorsey'], expertise: 'social media' },
|
|
54
|
+
content: { legends: ['gary-vaynerchuk', 'seth-godin'], expertise: 'content marketing' },
|
|
55
|
+
// Personal Development
|
|
56
|
+
productiv: { legends: ['tim-ferriss', 'naval-ravikant'], expertise: 'productivity' },
|
|
57
|
+
wealth: { legends: ['naval-ravikant', 'warren-buffett', 'charlie-munger'], expertise: 'wealth building' },
|
|
58
|
+
success: { legends: ['naval-ravikant', 'tim-ferriss'], expertise: 'success principles' },
|
|
59
|
+
happiness: { legends: ['naval-ravikant', 'tim-ferriss'], expertise: 'happiness' },
|
|
60
|
+
philosophy: { legends: ['naval-ravikant', 'charlie-munger', 'ray-dalio'], expertise: 'philosophy' },
|
|
61
|
+
decision: { legends: ['charlie-munger', 'ray-dalio', 'jeff-bezos'], expertise: 'decision making' },
|
|
62
|
+
mental: { legends: ['charlie-munger', 'naval-ravikant'], expertise: 'mental models' },
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Find matching legends based on question content
|
|
66
|
+
*/
|
|
67
|
+
function findMatches(question, maxMatches) {
|
|
68
|
+
const q = question.toLowerCase();
|
|
69
|
+
const matchScores = new Map();
|
|
70
|
+
// Score legends based on keyword matches
|
|
71
|
+
for (const [keyword, data] of Object.entries(EXPERTISE_KEYWORDS)) {
|
|
72
|
+
if (q.includes(keyword)) {
|
|
73
|
+
for (const legendId of data.legends) {
|
|
74
|
+
const current = matchScores.get(legendId) || { score: 0, reasons: [], expertise: [] };
|
|
75
|
+
current.score += 1;
|
|
76
|
+
if (!current.reasons.includes(data.expertise)) {
|
|
77
|
+
current.reasons.push(data.expertise);
|
|
78
|
+
}
|
|
79
|
+
current.expertise.push(data.expertise);
|
|
80
|
+
matchScores.set(legendId, current);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// If no keyword matches, use default diverse panel
|
|
85
|
+
if (matchScores.size === 0) {
|
|
86
|
+
return [
|
|
87
|
+
{ legendId: 'naval-ravikant', reason: 'general wisdom and mental models', matchedExpertise: ['general wisdom'] },
|
|
88
|
+
{ legendId: 'paul-graham', reason: 'startup and business insights', matchedExpertise: ['startups'] },
|
|
89
|
+
{ legendId: 'charlie-munger', reason: 'decision making and thinking', matchedExpertise: ['mental models'] },
|
|
90
|
+
].slice(0, maxMatches);
|
|
91
|
+
}
|
|
92
|
+
// Sort by score and return top matches
|
|
93
|
+
const sorted = Array.from(matchScores.entries())
|
|
94
|
+
.sort((a, b) => b[1].score - a[1].score)
|
|
95
|
+
.slice(0, maxMatches);
|
|
96
|
+
return sorted.map(([legendId, data]) => ({
|
|
97
|
+
legendId,
|
|
98
|
+
reason: data.reasons.join(', '),
|
|
99
|
+
matchedExpertise: [...new Set(data.expertise)],
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Extract a key insight from a legend's principles
|
|
104
|
+
*/
|
|
105
|
+
function extractKeyInsight(legend) {
|
|
106
|
+
// Try to get a relevant principle or pattern
|
|
107
|
+
if (legend.principles?.length) {
|
|
108
|
+
return safeString(legend.principles[0], '');
|
|
109
|
+
}
|
|
110
|
+
if (legend.patterns?.length) {
|
|
111
|
+
const pattern = legend.patterns[0];
|
|
112
|
+
if (typeof pattern === 'object' && pattern.name) {
|
|
113
|
+
return `${pattern.name}: ${safeString(pattern.description || '', '')}`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (legend.anti_patterns?.length) {
|
|
117
|
+
const ap = legend.anti_patterns[0];
|
|
118
|
+
const apText = typeof ap === 'object' ? ap.pattern : String(ap);
|
|
119
|
+
return `Avoids: ${safeString(apText, '')}`;
|
|
120
|
+
}
|
|
121
|
+
return `${legend.name}'s perspective on ${(legend.owns || ['business']).slice(0, 2).join(' and ')}`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Auto-match legends to a question
|
|
125
|
+
*/
|
|
126
|
+
export function autoMatch(input) {
|
|
127
|
+
const { sanitized: question } = sanitizeContext(input.question);
|
|
128
|
+
const maxMatches = Math.min(input.max_matches || 2, 3);
|
|
129
|
+
const includePrompts = input.include_prompts || false;
|
|
130
|
+
// Find matching legends
|
|
131
|
+
const matches = findMatches(question, maxMatches);
|
|
132
|
+
// Build response
|
|
133
|
+
const matchedLegends = matches.map(match => {
|
|
134
|
+
const legend = getLegendById(match.legendId);
|
|
135
|
+
if (!legend)
|
|
136
|
+
return null;
|
|
137
|
+
const result = {
|
|
138
|
+
legend_id: legend.id,
|
|
139
|
+
name: legend.name,
|
|
140
|
+
relevance_reason: match.reason,
|
|
141
|
+
expertise_match: match.matchedExpertise,
|
|
142
|
+
key_insight: extractKeyInsight(legend),
|
|
143
|
+
};
|
|
144
|
+
if (includePrompts) {
|
|
145
|
+
result.system_prompt = buildLegendSystemPrompt(legend);
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
}).filter((m) => m !== null);
|
|
149
|
+
if (matchedLegends.length === 0) {
|
|
150
|
+
throw new Error('No matching legends found. Try rephrasing your question.');
|
|
151
|
+
}
|
|
152
|
+
// Generate suggested approach
|
|
153
|
+
const legendNames = matchedLegends.map(m => m.name);
|
|
154
|
+
const suggestedApproach = generateApproach(question, matchedLegends);
|
|
155
|
+
return {
|
|
156
|
+
question: escapeBackticks(question),
|
|
157
|
+
matches: matchedLegends,
|
|
158
|
+
suggested_approach: suggestedApproach,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Generate a suggested approach based on matched legends
|
|
163
|
+
*/
|
|
164
|
+
function generateApproach(question, matches) {
|
|
165
|
+
if (matches.length === 1) {
|
|
166
|
+
return `Consider ${matches[0].name}'s perspective on ${matches[0].expertise_match.join(', ')}. Their insight: "${matches[0].key_insight}"`;
|
|
167
|
+
}
|
|
168
|
+
const names = matches.map(m => m.name).join(' and ');
|
|
169
|
+
const expertise = [...new Set(matches.flatMap(m => m.expertise_match))].join(', ');
|
|
170
|
+
return `Combine perspectives from ${names} (expertise: ${expertise}). Each brings unique frameworks - use \`summon_legend\` or \`party_mode\` to get their full responses.`;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Format auto-match response for display
|
|
174
|
+
*/
|
|
175
|
+
export function formatAutoMatch(response) {
|
|
176
|
+
const lines = [
|
|
177
|
+
'# Auto-Matched Legends',
|
|
178
|
+
'',
|
|
179
|
+
`**Your Question:** ${response.question}`,
|
|
180
|
+
'',
|
|
181
|
+
'---',
|
|
182
|
+
'',
|
|
183
|
+
'## Best Matches',
|
|
184
|
+
'',
|
|
185
|
+
];
|
|
186
|
+
response.matches.forEach((match, i) => {
|
|
187
|
+
lines.push(`### ${i + 1}. ${match.name}`);
|
|
188
|
+
lines.push(`- **Why matched:** ${match.relevance_reason}`);
|
|
189
|
+
lines.push(`- **Expertise:** ${match.expertise_match.join(', ')}`);
|
|
190
|
+
lines.push(`- **Key insight:** "${escapeBackticks(match.key_insight)}"`);
|
|
191
|
+
if (match.system_prompt) {
|
|
192
|
+
lines.push('');
|
|
193
|
+
lines.push('<details>');
|
|
194
|
+
lines.push('<summary>Full System Prompt</summary>');
|
|
195
|
+
lines.push('');
|
|
196
|
+
lines.push('```');
|
|
197
|
+
lines.push(escapeBackticks(match.system_prompt));
|
|
198
|
+
lines.push('```');
|
|
199
|
+
lines.push('</details>');
|
|
200
|
+
}
|
|
201
|
+
lines.push('');
|
|
202
|
+
});
|
|
203
|
+
lines.push('---');
|
|
204
|
+
lines.push('');
|
|
205
|
+
lines.push('## Suggested Approach');
|
|
206
|
+
lines.push('');
|
|
207
|
+
lines.push(response.suggested_approach);
|
|
208
|
+
lines.push('');
|
|
209
|
+
lines.push('---');
|
|
210
|
+
lines.push('');
|
|
211
|
+
lines.push('**Next Steps:**');
|
|
212
|
+
lines.push('- Use `summon_legend` to get advice from a specific legend');
|
|
213
|
+
lines.push('- Use `party_mode` to have all matched legends discuss together');
|
|
214
|
+
lines.push('');
|
|
215
|
+
lines.push('*DISCLAIMER: AI personas for educational purposes. Not affiliated with real individuals.*');
|
|
216
|
+
return lines.join('\n');
|
|
217
|
+
}
|
|
218
|
+
// MCP Tool Definition
|
|
219
|
+
export const autoMatchTool = {
|
|
220
|
+
name: 'auto_match',
|
|
221
|
+
description: `Automatically find the most relevant legends for your question.
|
|
222
|
+
|
|
223
|
+
**How it works:**
|
|
224
|
+
1. Analyzes your question for topics and keywords
|
|
225
|
+
2. Matches relevant legends based on their expertise
|
|
226
|
+
3. Returns matched legends with key insights
|
|
227
|
+
|
|
228
|
+
**Use this when:**
|
|
229
|
+
- You're not sure which legend to ask
|
|
230
|
+
- You want to see who has expertise in your topic
|
|
231
|
+
- You want quick insights before a full conversation
|
|
232
|
+
|
|
233
|
+
**What you get:**
|
|
234
|
+
- Top 2-3 most relevant legends
|
|
235
|
+
- Why each was matched
|
|
236
|
+
- A key insight from each
|
|
237
|
+
- Suggested next steps
|
|
238
|
+
|
|
239
|
+
**Examples:**
|
|
240
|
+
- "How do I raise money for my startup?" → Paul Graham, Marc Andreessen
|
|
241
|
+
- "What's the future of AI?" → Sam Altman, Jensen Huang
|
|
242
|
+
- "How should I think about risk?" → Ray Dalio, Howard Marks
|
|
243
|
+
|
|
244
|
+
DISCLAIMER: AI personas for educational purposes only.`,
|
|
245
|
+
inputSchema: {
|
|
246
|
+
type: 'object',
|
|
247
|
+
properties: {
|
|
248
|
+
question: {
|
|
249
|
+
type: 'string',
|
|
250
|
+
description: 'Your question or topic to find relevant legends for',
|
|
251
|
+
},
|
|
252
|
+
max_matches: {
|
|
253
|
+
type: 'number',
|
|
254
|
+
description: 'Maximum legends to match (default: 2, max: 3)',
|
|
255
|
+
},
|
|
256
|
+
include_prompts: {
|
|
257
|
+
type: 'boolean',
|
|
258
|
+
description: 'Include full system prompts for each legend (default: false)',
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
required: ['question'],
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
//# sourceMappingURL=auto-match.js.map
|