thorbit-content-mcp 0.1.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.
@@ -0,0 +1,320 @@
1
+ #!/usr/bin/env node
2
+
3
+ // bin/thorbit-content-mcp-install.ts
4
+ import { realpathSync } from "fs";
5
+ import { fileURLToPath } from "url";
6
+ function readArg(name, argv = process.argv.slice(2)) {
7
+ const prefix = `--${name}=`;
8
+ const inline = argv.find((arg) => arg.startsWith(prefix));
9
+ if (inline) return inline.slice(prefix.length);
10
+ const index = argv.indexOf(`--${name}`);
11
+ return index >= 0 ? argv[index + 1] : void 0;
12
+ }
13
+ function hasFlag(name, argv = process.argv.slice(2)) {
14
+ return argv.includes(`--${name}`);
15
+ }
16
+ function resolveInstallOptions(argv = process.argv.slice(2)) {
17
+ return {
18
+ apiKey: readArg("api-key", argv) ?? process.env.THORBIT_API_KEY ?? process.env.THORBIT_MCP_API_KEY ?? "thbt_mcp_...",
19
+ baseUrl: readArg("base-url", argv) ?? process.env.THORBIT_BASE_URL,
20
+ keyPath: readArg("key-path", argv) ?? process.env.THORBIT_CONTENT_MCP_KEY_PATH,
21
+ serverName: readArg("server-name", argv) ?? "thorbit-content",
22
+ json: hasFlag("json", argv),
23
+ color: !hasFlag("no-color", argv),
24
+ help: hasFlag("help", argv) || hasFlag("h", argv)
25
+ };
26
+ }
27
+ function buildMcpConfig(options) {
28
+ const env = options.keyPath ? { THORBIT_CONTENT_MCP_KEY_PATH: options.keyPath } : { THORBIT_API_KEY: options.apiKey };
29
+ if (options.baseUrl) {
30
+ env.THORBIT_BASE_URL = options.baseUrl.replace(/\/$/, "");
31
+ }
32
+ return {
33
+ mcpServers: {
34
+ [options.serverName]: {
35
+ command: "npx",
36
+ args: ["-y", "thorbit-content-mcp@latest"],
37
+ env
38
+ }
39
+ }
40
+ };
41
+ }
42
+ var ansi = {
43
+ reset: "\x1B[0m",
44
+ bold: "\x1B[1m",
45
+ dim: "\x1B[2m",
46
+ terracotta: "\x1B[38;2;198;90;54m",
47
+ cardBg: "\x1B[48;2;31;31;31m",
48
+ cardBorder: "\x1B[38;2;108;66;50m",
49
+ clay: "\x1B[38;2;226;138;92m",
50
+ cream: "\x1B[38;2;255;244;230m",
51
+ green: "\x1B[38;2;75;181;67m",
52
+ shadow: "\x1B[38;2;116;70;56m",
53
+ slate: "\x1B[38;2;174;181;195m",
54
+ wire: "\x1B[38;2;198;90;54m"
55
+ };
56
+ function paint(value, code, enabled) {
57
+ return enabled ? `${code}${value}${ansi.reset}` : value;
58
+ }
59
+ function renderHelp(color) {
60
+ return [
61
+ renderBanner(color),
62
+ "",
63
+ "Usage:",
64
+ " npx -y -p thorbit-content-mcp@latest thorbit-content-mcp-install [options]",
65
+ "",
66
+ "Options:",
67
+ " --api-key <key> Thorbit MCP API key from Settings -> MCPs.",
68
+ " --key-path <path> File containing the API key. Preferred for shared machines and servers.",
69
+ " --base-url <url> Thorbit app URL. Defaults to https://thorbit.ai.",
70
+ " --server-name <name> MCP client server name. Defaults to thorbit-content.",
71
+ " --json Print only MCP client JSON.",
72
+ " --no-color Disable ANSI color.",
73
+ " --help Show this help.",
74
+ ""
75
+ ].join("\n");
76
+ }
77
+ function renderBanner(color) {
78
+ return color ? renderReferenceCardBanner() : renderAsciiBanner();
79
+ }
80
+ var cardWidth = 104;
81
+ var thorbitPixelLetters = {
82
+ T: [
83
+ "1111111",
84
+ "0011000",
85
+ "0011000",
86
+ "0011000",
87
+ "0011000",
88
+ "0011000",
89
+ "0011000"
90
+ ],
91
+ H: [
92
+ "110011",
93
+ "110011",
94
+ "110011",
95
+ "111111",
96
+ "110011",
97
+ "110011",
98
+ "110011"
99
+ ],
100
+ O: [
101
+ "011110",
102
+ "110011",
103
+ "110011",
104
+ "110011",
105
+ "110011",
106
+ "110011",
107
+ "011110"
108
+ ],
109
+ R: [
110
+ "111110",
111
+ "110011",
112
+ "110011",
113
+ "111110",
114
+ "110110",
115
+ "110011",
116
+ "110011"
117
+ ],
118
+ B: [
119
+ "111110",
120
+ "110011",
121
+ "110011",
122
+ "111110",
123
+ "110011",
124
+ "110011",
125
+ "111110"
126
+ ],
127
+ I: [
128
+ "1111",
129
+ "0110",
130
+ "0110",
131
+ "0110",
132
+ "0110",
133
+ "0110",
134
+ "1111"
135
+ ]
136
+ };
137
+ function renderReferenceCardBanner() {
138
+ const heading = justifyCardContent(" Plain text", "\u29C9", cardWidth);
139
+ return [
140
+ `${ansi.cardBorder}\u256D${"\u2500".repeat(cardWidth + 2)}\u256E${ansi.reset}`,
141
+ renderCardLine(heading, "title"),
142
+ renderCardLine("", "blank"),
143
+ ...renderLayeredPixelWordmarkCardLines("THORBIT"),
144
+ renderCardLine("", "blank"),
145
+ renderCardLine(" THORBIT CONTENT MCP", "caption"),
146
+ `${ansi.cardBorder}\u2570${"\u2500".repeat(cardWidth + 2)}\u256F${ansi.reset}`
147
+ ].join("\n");
148
+ }
149
+ function renderAsciiBanner() {
150
+ const contentWidth = 96;
151
+ const border = `+${"-".repeat(contentWidth + 2)}+`;
152
+ const cardLine = (content = "") => `| ${content.padEnd(contentWidth)} |`;
153
+ const title = "Plain text";
154
+ const copy = "[copy]";
155
+ const lines = [
156
+ border,
157
+ cardLine(`${title}${" ".repeat(contentWidth - title.length - copy.length)}${copy}`),
158
+ cardLine(),
159
+ ...renderAsciiWordmark("THORBIT").map((line) => cardLine(` ${line}`)),
160
+ cardLine(),
161
+ cardLine(" THORBIT CONTENT MCP"),
162
+ border
163
+ ];
164
+ return lines.join("\n");
165
+ }
166
+ function renderCardLine(content, tone) {
167
+ const foreground = {
168
+ title: ansi.cream,
169
+ mark: ansi.clay,
170
+ wire: ansi.wire,
171
+ caption: ansi.terracotta,
172
+ blank: ansi.slate
173
+ }[tone];
174
+ return renderCardLineParts([{ color: foreground, text: content }]);
175
+ }
176
+ function renderCardLineParts(parts) {
177
+ const visibleWidth = parts.reduce((width, part) => width + part.text.length, 0);
178
+ const padding = " ".repeat(Math.max(0, cardWidth - visibleWidth));
179
+ return [
180
+ `${ansi.cardBorder}\u2502${ansi.reset}`,
181
+ ansi.cardBg,
182
+ " ",
183
+ ...parts.map((part) => `${part.color}${part.text}`),
184
+ padding,
185
+ " ",
186
+ ansi.reset,
187
+ `${ansi.cardBorder}\u2502${ansi.reset}`
188
+ ].join("");
189
+ }
190
+ function justifyCardContent(left, right, width) {
191
+ return `${left}${" ".repeat(Math.max(1, width - left.length - right.length))}${right}`;
192
+ }
193
+ function buildPixelWordmarkMatrix(word) {
194
+ const rows = Array.from({ length: 7 }, () => []);
195
+ for (let rowIndex = 0; rowIndex < 7; rowIndex += 1) {
196
+ word.split("").forEach((letter, letterIndex) => {
197
+ const pattern = thorbitPixelLetters[letter];
198
+ if (!pattern) return;
199
+ if (letterIndex > 0) rows[rowIndex].push(false);
200
+ rows[rowIndex].push(...pattern[rowIndex].split("").map((cell) => cell === "1"));
201
+ });
202
+ }
203
+ return rows;
204
+ }
205
+ function renderLayeredPixelWordmarkCardLines(word) {
206
+ const matrix = buildPixelWordmarkMatrix(word);
207
+ const wordmarkWidth = Math.max(...matrix.map((row) => row.length));
208
+ const shadowOffsetX = 1;
209
+ const shadowOffsetY = -1;
210
+ const outputRows = matrix.length + Math.abs(Math.min(0, shadowOffsetY));
211
+ const outputWidth = wordmarkWidth + shadowOffsetX;
212
+ return Array.from({ length: outputRows }, (_, outputRowIndex) => {
213
+ const rowParts = [{ color: ansi.slate, text: " " }];
214
+ for (let columnIndex = 0; columnIndex < outputWidth; columnIndex += 1) {
215
+ const foregroundRow = outputRowIndex - Math.abs(Math.min(0, shadowOffsetY));
216
+ const hasForeground = foregroundRow >= 0 && Boolean(matrix[foregroundRow]?.[columnIndex]);
217
+ const shadowSourceRow = outputRowIndex - shadowOffsetY - Math.abs(Math.min(0, shadowOffsetY));
218
+ const shadowSourceColumn = columnIndex - shadowOffsetX;
219
+ const hasShadow = shadowSourceRow >= 0 && shadowSourceColumn >= 0 && Boolean(matrix[shadowSourceRow]?.[shadowSourceColumn]);
220
+ if (hasForeground) {
221
+ appendCardPart(rowParts, ansi.clay, "\u2588\u2588");
222
+ } else if (hasShadow) {
223
+ appendCardPart(rowParts, ansi.shadow, "\u2591\u2591");
224
+ } else {
225
+ appendCardPart(rowParts, ansi.slate, " ");
226
+ }
227
+ }
228
+ return renderCardLineParts(rowParts);
229
+ });
230
+ }
231
+ function appendCardPart(parts, color, text) {
232
+ const previous = parts[parts.length - 1];
233
+ if (previous?.color === color) {
234
+ previous.text += text;
235
+ return;
236
+ }
237
+ parts.push({ color, text });
238
+ }
239
+ function renderAsciiWordmark(word) {
240
+ const matrix = buildPixelWordmarkMatrix(word);
241
+ const rows = [];
242
+ for (const matrixRow of matrix) {
243
+ rows.push(matrixRow.map((cell) => cell ? "#" : " ").join(""));
244
+ }
245
+ return rows;
246
+ }
247
+ function renderInstallInstructions(options) {
248
+ const color = options.color;
249
+ const json = JSON.stringify(buildMcpConfig(options), null, 2);
250
+ const keySetup = options.keyPath ? [
251
+ paint("Key file mode", ansi.terracotta, color),
252
+ ` chmod 600 ${options.keyPath}`
253
+ ].join("\n") : [
254
+ paint("Safer key-file option", ansi.terracotta, color),
255
+ " mkdir -p ~/.config/thorbit",
256
+ " printf '%s\\n' 'thbt_mcp_...' > ~/.config/thorbit/content-mcp-key",
257
+ " chmod 600 ~/.config/thorbit/content-mcp-key",
258
+ "",
259
+ " Then run:",
260
+ " npx -y -p thorbit-content-mcp@latest thorbit-content-mcp-install --key-path ~/.config/thorbit/content-mcp-key"
261
+ ].join("\n");
262
+ return [
263
+ renderBanner(color),
264
+ paint("MCP Scraper research and on-page analysis for MCP agents.", ansi.slate, color),
265
+ "",
266
+ `${paint("1.", ansi.terracotta, color)} Generate an API key in Thorbit Settings -> MCPs with content/onpage scopes.`,
267
+ `${paint("2.", ansi.terracotta, color)} Add this server config to your MCP client:`,
268
+ "",
269
+ json,
270
+ "",
271
+ `${paint("3.", ansi.terracotta, color)} Restart your MCP client, then call ${paint("thorbit_content_harvest_serp", ansi.green, color)} or ${paint("thorbit_content_reddit_research", ansi.green, color)}.`,
272
+ "",
273
+ keySetup,
274
+ "",
275
+ paint("Tools", ansi.terracotta, color),
276
+ " thorbit_content_extract_url Extract a URL through MCP Scraper.",
277
+ " thorbit_content_harvest_serp Harvest SERP/PAA evidence through MCP Scraper.",
278
+ " thorbit_content_reddit_research Read Reddit through MCP Scraper browser-agent.",
279
+ " thorbit_onpage_start_analysis Start durable Thorbit on-page analysis.",
280
+ " thorbit_onpage_get_analysis Read analysis status and summary.",
281
+ "",
282
+ paint("Raw JSON:", ansi.slate, color),
283
+ " npx -y -p thorbit-content-mcp@latest thorbit-content-mcp-install --json --api-key thbt_mcp_...",
284
+ ""
285
+ ].join("\n");
286
+ }
287
+ function main() {
288
+ const options = resolveInstallOptions();
289
+ if (options.help) {
290
+ process.stdout.write(renderHelp(options.color));
291
+ return;
292
+ }
293
+ if (options.json) {
294
+ process.stdout.write(`${JSON.stringify(buildMcpConfig(options), null, 2)}
295
+ `);
296
+ return;
297
+ }
298
+ process.stdout.write(renderInstallInstructions(options));
299
+ }
300
+ function isMainModule() {
301
+ const entry = process.argv[1];
302
+ if (!entry) return false;
303
+ try {
304
+ return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url));
305
+ } catch {
306
+ return false;
307
+ }
308
+ }
309
+ if (isMainModule()) main();
310
+ export {
311
+ buildMcpConfig,
312
+ hasFlag,
313
+ main,
314
+ readArg,
315
+ renderBanner,
316
+ renderHelp,
317
+ renderInstallInstructions,
318
+ resolveInstallOptions
319
+ };
320
+ //# sourceMappingURL=thorbit-content-mcp-install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../bin/thorbit-content-mcp-install.ts"],"sourcesContent":["import { realpathSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\n\nexport type InstallOptions = {\n apiKey: string\n baseUrl?: string\n keyPath?: string\n serverName: string\n json: boolean\n color: boolean\n help: boolean\n}\n\nexport function readArg(name: string, argv = process.argv.slice(2)): string | undefined {\n const prefix = `--${name}=`\n const inline = argv.find(arg => arg.startsWith(prefix))\n if (inline) return inline.slice(prefix.length)\n const index = argv.indexOf(`--${name}`)\n return index >= 0 ? argv[index + 1] : undefined\n}\n\nexport function hasFlag(name: string, argv = process.argv.slice(2)): boolean {\n return argv.includes(`--${name}`)\n}\n\nexport function resolveInstallOptions(argv = process.argv.slice(2)): InstallOptions {\n return {\n apiKey: readArg('api-key', argv) ?? process.env.THORBIT_API_KEY ?? process.env.THORBIT_MCP_API_KEY ?? 'thbt_mcp_...',\n baseUrl: readArg('base-url', argv) ?? process.env.THORBIT_BASE_URL,\n keyPath: readArg('key-path', argv) ?? process.env.THORBIT_CONTENT_MCP_KEY_PATH,\n serverName: readArg('server-name', argv) ?? 'thorbit-content',\n json: hasFlag('json', argv),\n color: !hasFlag('no-color', argv),\n help: hasFlag('help', argv) || hasFlag('h', argv),\n }\n}\n\nexport function buildMcpConfig(options: InstallOptions) {\n const env: Record<string, string> = options.keyPath\n ? { THORBIT_CONTENT_MCP_KEY_PATH: options.keyPath }\n : { THORBIT_API_KEY: options.apiKey }\n\n if (options.baseUrl) {\n env.THORBIT_BASE_URL = options.baseUrl.replace(/\\/$/, '')\n }\n\n return {\n mcpServers: {\n [options.serverName]: {\n command: 'npx',\n args: ['-y', 'thorbit-content-mcp@latest'],\n env,\n },\n },\n }\n}\n\nconst ansi = {\n reset: '\\u001b[0m',\n bold: '\\u001b[1m',\n dim: '\\u001b[2m',\n terracotta: '\\u001b[38;2;198;90;54m',\n cardBg: '\\u001b[48;2;31;31;31m',\n cardBorder: '\\u001b[38;2;108;66;50m',\n clay: '\\u001b[38;2;226;138;92m',\n cream: '\\u001b[38;2;255;244;230m',\n green: '\\u001b[38;2;75;181;67m',\n shadow: '\\u001b[38;2;116;70;56m',\n slate: '\\u001b[38;2;174;181;195m',\n wire: '\\u001b[38;2;198;90;54m',\n}\n\nfunction paint(value: string, code: string, enabled: boolean): string {\n return enabled ? `${code}${value}${ansi.reset}` : value\n}\n\nexport function renderHelp(color: boolean): string {\n return [\n renderBanner(color),\n '',\n 'Usage:',\n ' npx -y -p thorbit-content-mcp@latest thorbit-content-mcp-install [options]',\n '',\n 'Options:',\n ' --api-key <key> Thorbit MCP API key from Settings -> MCPs.',\n ' --key-path <path> File containing the API key. Preferred for shared machines and servers.',\n ' --base-url <url> Thorbit app URL. Defaults to https://thorbit.ai.',\n ' --server-name <name> MCP client server name. Defaults to thorbit-content.',\n ' --json Print only MCP client JSON.',\n ' --no-color Disable ANSI color.',\n ' --help Show this help.',\n '',\n ].join('\\n')\n}\n\nexport function renderBanner(color: boolean): string {\n return color ? renderReferenceCardBanner() : renderAsciiBanner()\n}\n\nconst cardWidth = 104\n\ntype CardTone = 'title' | 'mark' | 'wire' | 'caption' | 'blank'\ntype CardPart = {\n color: string\n text: string\n}\n\nconst thorbitPixelLetters: Record<string, string[]> = {\n T: [\n '1111111',\n '0011000',\n '0011000',\n '0011000',\n '0011000',\n '0011000',\n '0011000',\n ],\n H: [\n '110011',\n '110011',\n '110011',\n '111111',\n '110011',\n '110011',\n '110011',\n ],\n O: [\n '011110',\n '110011',\n '110011',\n '110011',\n '110011',\n '110011',\n '011110',\n ],\n R: [\n '111110',\n '110011',\n '110011',\n '111110',\n '110110',\n '110011',\n '110011',\n ],\n B: [\n '111110',\n '110011',\n '110011',\n '111110',\n '110011',\n '110011',\n '111110',\n ],\n I: [\n '1111',\n '0110',\n '0110',\n '0110',\n '0110',\n '0110',\n '1111',\n ],\n}\n\nfunction renderReferenceCardBanner(): string {\n const heading = justifyCardContent(' Plain text', '⧉', cardWidth)\n\n return [\n `${ansi.cardBorder}╭${'─'.repeat(cardWidth + 2)}╮${ansi.reset}`,\n renderCardLine(heading, 'title'),\n renderCardLine('', 'blank'),\n ...renderLayeredPixelWordmarkCardLines('THORBIT'),\n renderCardLine('', 'blank'),\n renderCardLine(' THORBIT CONTENT MCP', 'caption'),\n `${ansi.cardBorder}╰${'─'.repeat(cardWidth + 2)}╯${ansi.reset}`,\n ].join('\\n')\n}\n\nfunction renderAsciiBanner(): string {\n const contentWidth = 96\n const border = `+${'-'.repeat(contentWidth + 2)}+`\n const cardLine = (content = '') => `| ${content.padEnd(contentWidth)} |`\n const title = 'Plain text'\n const copy = '[copy]'\n const lines = [\n border,\n cardLine(`${title}${' '.repeat(contentWidth - title.length - copy.length)}${copy}`),\n cardLine(),\n ...renderAsciiWordmark('THORBIT').map(line => cardLine(` ${line}`)),\n cardLine(),\n cardLine(' THORBIT CONTENT MCP'),\n border,\n ]\n\n return lines.join('\\n')\n}\n\nfunction renderCardLine(content: string, tone: CardTone): string {\n const foreground = {\n title: ansi.cream,\n mark: ansi.clay,\n wire: ansi.wire,\n caption: ansi.terracotta,\n blank: ansi.slate,\n }[tone]\n return renderCardLineParts([{ color: foreground, text: content }])\n}\n\nfunction renderCardLineParts(parts: CardPart[]): string {\n const visibleWidth = parts.reduce((width, part) => width + part.text.length, 0)\n const padding = ' '.repeat(Math.max(0, cardWidth - visibleWidth))\n return [\n `${ansi.cardBorder}│${ansi.reset}`,\n ansi.cardBg,\n ' ',\n ...parts.map(part => `${part.color}${part.text}`),\n padding,\n ' ',\n ansi.reset,\n `${ansi.cardBorder}│${ansi.reset}`,\n ].join('')\n}\n\nfunction justifyCardContent(left: string, right: string, width: number): string {\n return `${left}${' '.repeat(Math.max(1, width - left.length - right.length))}${right}`\n}\n\nfunction buildPixelWordmarkMatrix(word: string): boolean[][] {\n const rows: boolean[][] = Array.from({ length: 7 }, () => [])\n for (let rowIndex = 0; rowIndex < 7; rowIndex += 1) {\n word.split('').forEach((letter, letterIndex) => {\n const pattern = thorbitPixelLetters[letter]\n if (!pattern) return\n if (letterIndex > 0) rows[rowIndex].push(false)\n rows[rowIndex].push(...pattern[rowIndex].split('').map(cell => cell === '1'))\n })\n }\n return rows\n}\n\nfunction renderLayeredPixelWordmarkCardLines(word: string): string[] {\n const matrix = buildPixelWordmarkMatrix(word)\n const wordmarkWidth = Math.max(...matrix.map(row => row.length))\n const shadowOffsetX = 1\n const shadowOffsetY = -1\n const outputRows = matrix.length + Math.abs(Math.min(0, shadowOffsetY))\n const outputWidth = wordmarkWidth + shadowOffsetX\n\n return Array.from({ length: outputRows }, (_, outputRowIndex) => {\n const rowParts: CardPart[] = [{ color: ansi.slate, text: ' ' }]\n\n for (let columnIndex = 0; columnIndex < outputWidth; columnIndex += 1) {\n const foregroundRow = outputRowIndex - Math.abs(Math.min(0, shadowOffsetY))\n const hasForeground = foregroundRow >= 0 && Boolean(matrix[foregroundRow]?.[columnIndex])\n const shadowSourceRow = outputRowIndex - shadowOffsetY - Math.abs(Math.min(0, shadowOffsetY))\n const shadowSourceColumn = columnIndex - shadowOffsetX\n const hasShadow = shadowSourceRow >= 0 && shadowSourceColumn >= 0\n && Boolean(matrix[shadowSourceRow]?.[shadowSourceColumn])\n\n if (hasForeground) {\n appendCardPart(rowParts, ansi.clay, '██')\n } else if (hasShadow) {\n appendCardPart(rowParts, ansi.shadow, '░░')\n } else {\n appendCardPart(rowParts, ansi.slate, ' ')\n }\n }\n\n return renderCardLineParts(rowParts)\n })\n}\n\nfunction appendCardPart(parts: CardPart[], color: string, text: string): void {\n const previous = parts[parts.length - 1]\n if (previous?.color === color) {\n previous.text += text\n return\n }\n parts.push({ color, text })\n}\n\nfunction renderAsciiWordmark(word: string): string[] {\n const matrix = buildPixelWordmarkMatrix(word)\n const rows: string[] = []\n for (const matrixRow of matrix) {\n rows.push(matrixRow.map(cell => (cell ? '#' : ' ')).join(''))\n }\n return rows\n}\n\nexport function renderInstallInstructions(options: InstallOptions): string {\n const color = options.color\n const json = JSON.stringify(buildMcpConfig(options), null, 2)\n const keySetup = options.keyPath\n ? [\n paint('Key file mode', ansi.terracotta, color),\n ` chmod 600 ${options.keyPath}`,\n ].join('\\n')\n : [\n paint('Safer key-file option', ansi.terracotta, color),\n ' mkdir -p ~/.config/thorbit',\n \" printf '%s\\\\n' 'thbt_mcp_...' > ~/.config/thorbit/content-mcp-key\",\n ' chmod 600 ~/.config/thorbit/content-mcp-key',\n '',\n ' Then run:',\n ' npx -y -p thorbit-content-mcp@latest thorbit-content-mcp-install --key-path ~/.config/thorbit/content-mcp-key',\n ].join('\\n')\n\n return [\n renderBanner(color),\n paint('MCP Scraper research and on-page analysis for MCP agents.', ansi.slate, color),\n '',\n `${paint('1.', ansi.terracotta, color)} Generate an API key in Thorbit Settings -> MCPs with content/onpage scopes.`,\n `${paint('2.', ansi.terracotta, color)} Add this server config to your MCP client:`,\n '',\n json,\n '',\n `${paint('3.', ansi.terracotta, color)} Restart your MCP client, then call ${paint('thorbit_content_harvest_serp', ansi.green, color)} or ${paint('thorbit_content_reddit_research', ansi.green, color)}.`,\n '',\n keySetup,\n '',\n paint('Tools', ansi.terracotta, color),\n ' thorbit_content_extract_url Extract a URL through MCP Scraper.',\n ' thorbit_content_harvest_serp Harvest SERP/PAA evidence through MCP Scraper.',\n ' thorbit_content_reddit_research Read Reddit through MCP Scraper browser-agent.',\n ' thorbit_onpage_start_analysis Start durable Thorbit on-page analysis.',\n ' thorbit_onpage_get_analysis Read analysis status and summary.',\n '',\n paint('Raw JSON:', ansi.slate, color),\n ' npx -y -p thorbit-content-mcp@latest thorbit-content-mcp-install --json --api-key thbt_mcp_...',\n '',\n ].join('\\n')\n}\n\nexport function main(): void {\n const options = resolveInstallOptions()\n if (options.help) {\n process.stdout.write(renderHelp(options.color))\n return\n }\n\n if (options.json) {\n process.stdout.write(`${JSON.stringify(buildMcpConfig(options), null, 2)}\\n`)\n return\n }\n\n process.stdout.write(renderInstallInstructions(options))\n}\n\nfunction isMainModule(): boolean {\n const entry = process.argv[1]\n if (!entry) return false\n try {\n return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url))\n } catch {\n return false\n }\n}\n\nif (isMainModule()) main()\n"],"mappings":";;;AAAA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAYvB,SAAS,QAAQ,MAAc,OAAO,QAAQ,KAAK,MAAM,CAAC,GAAuB;AACtF,QAAM,SAAS,KAAK,IAAI;AACxB,QAAM,SAAS,KAAK,KAAK,SAAO,IAAI,WAAW,MAAM,CAAC;AACtD,MAAI,OAAQ,QAAO,OAAO,MAAM,OAAO,MAAM;AAC7C,QAAM,QAAQ,KAAK,QAAQ,KAAK,IAAI,EAAE;AACtC,SAAO,SAAS,IAAI,KAAK,QAAQ,CAAC,IAAI;AACxC;AAEO,SAAS,QAAQ,MAAc,OAAO,QAAQ,KAAK,MAAM,CAAC,GAAY;AAC3E,SAAO,KAAK,SAAS,KAAK,IAAI,EAAE;AAClC;AAEO,SAAS,sBAAsB,OAAO,QAAQ,KAAK,MAAM,CAAC,GAAmB;AAClF,SAAO;AAAA,IACL,QAAQ,QAAQ,WAAW,IAAI,KAAK,QAAQ,IAAI,mBAAmB,QAAQ,IAAI,uBAAuB;AAAA,IACtG,SAAS,QAAQ,YAAY,IAAI,KAAK,QAAQ,IAAI;AAAA,IAClD,SAAS,QAAQ,YAAY,IAAI,KAAK,QAAQ,IAAI;AAAA,IAClD,YAAY,QAAQ,eAAe,IAAI,KAAK;AAAA,IAC5C,MAAM,QAAQ,QAAQ,IAAI;AAAA,IAC1B,OAAO,CAAC,QAAQ,YAAY,IAAI;AAAA,IAChC,MAAM,QAAQ,QAAQ,IAAI,KAAK,QAAQ,KAAK,IAAI;AAAA,EAClD;AACF;AAEO,SAAS,eAAe,SAAyB;AACtD,QAAM,MAA8B,QAAQ,UACxC,EAAE,8BAA8B,QAAQ,QAAQ,IAChD,EAAE,iBAAiB,QAAQ,OAAO;AAEtC,MAAI,QAAQ,SAAS;AACnB,QAAI,mBAAmB,QAAQ,QAAQ,QAAQ,OAAO,EAAE;AAAA,EAC1D;AAEA,SAAO;AAAA,IACL,YAAY;AAAA,MACV,CAAC,QAAQ,UAAU,GAAG;AAAA,QACpB,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,4BAA4B;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,OAAO;AAAA,EACX,OAAO;AAAA,EACP,MAAM;AAAA,EACN,KAAK;AAAA,EACL,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AACR;AAEA,SAAS,MAAM,OAAe,MAAc,SAA0B;AACpE,SAAO,UAAU,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,KAAK,KAAK;AACpD;AAEO,SAAS,WAAW,OAAwB;AACjD,SAAO;AAAA,IACL,aAAa,KAAK;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEO,SAAS,aAAa,OAAwB;AACnD,SAAO,QAAQ,0BAA0B,IAAI,kBAAkB;AACjE;AAEA,IAAM,YAAY;AAQlB,IAAM,sBAAgD;AAAA,EACpD,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,4BAAoC;AAC3C,QAAM,UAAU,mBAAmB,gBAAgB,UAAK,SAAS;AAEjE,SAAO;AAAA,IACL,GAAG,KAAK,UAAU,SAAI,SAAI,OAAO,YAAY,CAAC,CAAC,SAAI,KAAK,KAAK;AAAA,IAC7D,eAAe,SAAS,OAAO;AAAA,IAC/B,eAAe,IAAI,OAAO;AAAA,IAC1B,GAAG,oCAAoC,SAAS;AAAA,IAChD,eAAe,IAAI,OAAO;AAAA,IAC1B,eAAe,yBAAyB,SAAS;AAAA,IACjD,GAAG,KAAK,UAAU,SAAI,SAAI,OAAO,YAAY,CAAC,CAAC,SAAI,KAAK,KAAK;AAAA,EAC/D,EAAE,KAAK,IAAI;AACb;AAEA,SAAS,oBAA4B;AACnC,QAAM,eAAe;AACrB,QAAM,SAAS,IAAI,IAAI,OAAO,eAAe,CAAC,CAAC;AAC/C,QAAM,WAAW,CAAC,UAAU,OAAO,KAAK,QAAQ,OAAO,YAAY,CAAC;AACpE,QAAM,QAAQ;AACd,QAAM,OAAO;AACb,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,SAAS,GAAG,KAAK,GAAG,IAAI,OAAO,eAAe,MAAM,SAAS,KAAK,MAAM,CAAC,GAAG,IAAI,EAAE;AAAA,IAClF,SAAS;AAAA,IACT,GAAG,oBAAoB,SAAS,EAAE,IAAI,UAAQ,SAAS,KAAK,IAAI,EAAE,CAAC;AAAA,IACnE,SAAS;AAAA,IACT,SAAS,uBAAuB;AAAA,IAChC;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,eAAe,SAAiB,MAAwB;AAC/D,QAAM,aAAa;AAAA,IACjB,OAAO,KAAK;AAAA,IACZ,MAAM,KAAK;AAAA,IACX,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,OAAO,KAAK;AAAA,EACd,EAAE,IAAI;AACN,SAAO,oBAAoB,CAAC,EAAE,OAAO,YAAY,MAAM,QAAQ,CAAC,CAAC;AACnE;AAEA,SAAS,oBAAoB,OAA2B;AACtD,QAAM,eAAe,MAAM,OAAO,CAAC,OAAO,SAAS,QAAQ,KAAK,KAAK,QAAQ,CAAC;AAC9E,QAAM,UAAU,IAAI,OAAO,KAAK,IAAI,GAAG,YAAY,YAAY,CAAC;AAChE,SAAO;AAAA,IACL,GAAG,KAAK,UAAU,SAAI,KAAK,KAAK;AAAA,IAChC,KAAK;AAAA,IACL;AAAA,IACA,GAAG,MAAM,IAAI,UAAQ,GAAG,KAAK,KAAK,GAAG,KAAK,IAAI,EAAE;AAAA,IAChD;AAAA,IACA;AAAA,IACA,KAAK;AAAA,IACL,GAAG,KAAK,UAAU,SAAI,KAAK,KAAK;AAAA,EAClC,EAAE,KAAK,EAAE;AACX;AAEA,SAAS,mBAAmB,MAAc,OAAe,OAAuB;AAC9E,SAAO,GAAG,IAAI,GAAG,IAAI,OAAO,KAAK,IAAI,GAAG,QAAQ,KAAK,SAAS,MAAM,MAAM,CAAC,CAAC,GAAG,KAAK;AACtF;AAEA,SAAS,yBAAyB,MAA2B;AAC3D,QAAM,OAAoB,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,CAAC;AAC5D,WAAS,WAAW,GAAG,WAAW,GAAG,YAAY,GAAG;AAClD,SAAK,MAAM,EAAE,EAAE,QAAQ,CAAC,QAAQ,gBAAgB;AAC9C,YAAM,UAAU,oBAAoB,MAAM;AAC1C,UAAI,CAAC,QAAS;AACd,UAAI,cAAc,EAAG,MAAK,QAAQ,EAAE,KAAK,KAAK;AAC9C,WAAK,QAAQ,EAAE,KAAK,GAAG,QAAQ,QAAQ,EAAE,MAAM,EAAE,EAAE,IAAI,UAAQ,SAAS,GAAG,CAAC;AAAA,IAC9E,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,oCAAoC,MAAwB;AACnE,QAAM,SAAS,yBAAyB,IAAI;AAC5C,QAAM,gBAAgB,KAAK,IAAI,GAAG,OAAO,IAAI,SAAO,IAAI,MAAM,CAAC;AAC/D,QAAM,gBAAgB;AACtB,QAAM,gBAAgB;AACtB,QAAM,aAAa,OAAO,SAAS,KAAK,IAAI,KAAK,IAAI,GAAG,aAAa,CAAC;AACtE,QAAM,cAAc,gBAAgB;AAEpC,SAAO,MAAM,KAAK,EAAE,QAAQ,WAAW,GAAG,CAAC,GAAG,mBAAmB;AAC/D,UAAM,WAAuB,CAAC,EAAE,OAAO,KAAK,OAAO,MAAM,OAAO,CAAC;AAEjE,aAAS,cAAc,GAAG,cAAc,aAAa,eAAe,GAAG;AACrE,YAAM,gBAAgB,iBAAiB,KAAK,IAAI,KAAK,IAAI,GAAG,aAAa,CAAC;AAC1E,YAAM,gBAAgB,iBAAiB,KAAK,QAAQ,OAAO,aAAa,IAAI,WAAW,CAAC;AACxF,YAAM,kBAAkB,iBAAiB,gBAAgB,KAAK,IAAI,KAAK,IAAI,GAAG,aAAa,CAAC;AAC5F,YAAM,qBAAqB,cAAc;AACzC,YAAM,YAAY,mBAAmB,KAAK,sBAAsB,KAC3D,QAAQ,OAAO,eAAe,IAAI,kBAAkB,CAAC;AAE1D,UAAI,eAAe;AACjB,uBAAe,UAAU,KAAK,MAAM,cAAI;AAAA,MAC1C,WAAW,WAAW;AACpB,uBAAe,UAAU,KAAK,QAAQ,cAAI;AAAA,MAC5C,OAAO;AACL,uBAAe,UAAU,KAAK,OAAO,IAAI;AAAA,MAC3C;AAAA,IACF;AAEA,WAAO,oBAAoB,QAAQ;AAAA,EACrC,CAAC;AACH;AAEA,SAAS,eAAe,OAAmB,OAAe,MAAoB;AAC5E,QAAM,WAAW,MAAM,MAAM,SAAS,CAAC;AACvC,MAAI,UAAU,UAAU,OAAO;AAC7B,aAAS,QAAQ;AACjB;AAAA,EACF;AACA,QAAM,KAAK,EAAE,OAAO,KAAK,CAAC;AAC5B;AAEA,SAAS,oBAAoB,MAAwB;AACnD,QAAM,SAAS,yBAAyB,IAAI;AAC5C,QAAM,OAAiB,CAAC;AACxB,aAAW,aAAa,QAAQ;AAC9B,SAAK,KAAK,UAAU,IAAI,UAAS,OAAO,MAAM,GAAI,EAAE,KAAK,EAAE,CAAC;AAAA,EAC9D;AACA,SAAO;AACT;AAEO,SAAS,0BAA0B,SAAiC;AACzE,QAAM,QAAQ,QAAQ;AACtB,QAAM,OAAO,KAAK,UAAU,eAAe,OAAO,GAAG,MAAM,CAAC;AAC5D,QAAM,WAAW,QAAQ,UACrB;AAAA,IACE,MAAM,iBAAiB,KAAK,YAAY,KAAK;AAAA,IAC7C,eAAe,QAAQ,OAAO;AAAA,EAChC,EAAE,KAAK,IAAI,IACX;AAAA,IACE,MAAM,yBAAyB,KAAK,YAAY,KAAK;AAAA,IACrD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AAEf,SAAO;AAAA,IACL,aAAa,KAAK;AAAA,IAClB,MAAM,6DAA6D,KAAK,OAAO,KAAK;AAAA,IACpF;AAAA,IACA,GAAG,MAAM,MAAM,KAAK,YAAY,KAAK,CAAC;AAAA,IACtC,GAAG,MAAM,MAAM,KAAK,YAAY,KAAK,CAAC;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG,MAAM,MAAM,KAAK,YAAY,KAAK,CAAC,uCAAuC,MAAM,gCAAgC,KAAK,OAAO,KAAK,CAAC,OAAO,MAAM,mCAAmC,KAAK,OAAO,KAAK,CAAC;AAAA,IACvM;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,SAAS,KAAK,YAAY,KAAK;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,aAAa,KAAK,OAAO,KAAK;AAAA,IACpC;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEO,SAAS,OAAa;AAC3B,QAAM,UAAU,sBAAsB;AACtC,MAAI,QAAQ,MAAM;AAChB,YAAQ,OAAO,MAAM,WAAW,QAAQ,KAAK,CAAC;AAC9C;AAAA,EACF;AAEA,MAAI,QAAQ,MAAM;AAChB,YAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,eAAe,OAAO,GAAG,MAAM,CAAC,CAAC;AAAA,CAAI;AAC5E;AAAA,EACF;AAEA,UAAQ,OAAO,MAAM,0BAA0B,OAAO,CAAC;AACzD;AAEA,SAAS,eAAwB;AAC/B,QAAM,QAAQ,QAAQ,KAAK,CAAC;AAC5B,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,WAAO,aAAa,KAAK,MAAM,aAAa,cAAc,YAAY,GAAG,CAAC;AAAA,EAC5E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAI,aAAa,EAAG,MAAK;","names":[]}
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // bin/thorbit-content-mcp.ts
5
+ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+
7
+ // src/content-onpage-api-client.ts
8
+ var ThorbitContentOnPageApiClient = class {
9
+ baseUrl;
10
+ apiKey;
11
+ constructor(options) {
12
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
13
+ this.apiKey = options.apiKey;
14
+ }
15
+ async callTool(toolName, input) {
16
+ const response = await fetch(`${this.baseUrl}/api/v1/mcp/content-onpage/${toolName}`, {
17
+ method: "POST",
18
+ headers: {
19
+ Authorization: `Bearer ${this.apiKey}`,
20
+ "Content-Type": "application/json"
21
+ },
22
+ body: JSON.stringify(input ?? {})
23
+ });
24
+ const body = await response.json().catch(() => null);
25
+ if (body && typeof body === "object" && "ok" in body) return body;
26
+ return {
27
+ ok: false,
28
+ requestId: "unavailable",
29
+ error: {
30
+ code: response.ok ? "invalid_response" : `http_${response.status}`,
31
+ message: response.ok ? "Thorbit returned an invalid MCP response" : `Thorbit API request failed with HTTP ${response.status}`
32
+ }
33
+ };
34
+ }
35
+ };
36
+
37
+ // src/content-onpage-mcp-env.ts
38
+ var import_node_fs = require("fs");
39
+ var import_node_os = require("os");
40
+ var import_node_path = require("path");
41
+ function readApiKeyFile() {
42
+ const explicitPath = process.env.THORBIT_CONTENT_MCP_KEY_PATH?.trim();
43
+ const paths = [explicitPath, (0, import_node_path.join)((0, import_node_os.homedir)(), ".thorbit-content-mcp-key")].filter(Boolean);
44
+ for (const path of paths) {
45
+ try {
46
+ const value = (0, import_node_fs.readFileSync)(path, "utf8").trim();
47
+ if (value) return value;
48
+ } catch {
49
+ continue;
50
+ }
51
+ }
52
+ return void 0;
53
+ }
54
+ function resolveThorbitContentOnPageMcpEnv() {
55
+ const apiKey = (process.env.THORBIT_API_KEY || process.env.THORBIT_MCP_API_KEY || process.env.THORBIT_CONTENT_MCP_API_KEY || readApiKeyFile())?.trim();
56
+ if (!apiKey) {
57
+ throw new Error("THORBIT_API_KEY, THORBIT_MCP_API_KEY, or ~/.thorbit-content-mcp-key is required");
58
+ }
59
+ return {
60
+ apiKey,
61
+ baseUrl: (process.env.THORBIT_BASE_URL || process.env.THORBIT_CONTENT_MCP_BASE_URL || "https://thorbit.ai").trim()
62
+ };
63
+ }
64
+
65
+ // src/content-onpage-mcp-server.ts
66
+ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
67
+
68
+ // src/content-onpage-mcp-response-formatters.ts
69
+ function formatThorbitContentOnPageMcpToolResult(toolName, envelope) {
70
+ const text = JSON.stringify({
71
+ toolName,
72
+ ...envelope
73
+ }, null, 2);
74
+ return {
75
+ content: [{ type: "text", text }],
76
+ isError: !envelope.ok
77
+ };
78
+ }
79
+
80
+ // src/content-onpage-mcp-tool-schemas.ts
81
+ var import_zod = require("zod");
82
+ var ThorbitContentExtractUrlInputSchema = {
83
+ url: import_zod.z.string().url().describe("Public URL to extract through MCP Scraper."),
84
+ browserFallback: import_zod.z.boolean().default(true).describe("Use MCP Scraper browser fallback for JS-heavy pages. Default true."),
85
+ extractBranding: import_zod.z.boolean().default(false).describe("Ask MCP Scraper to extract brand colors, fonts, logo, and favicon when supported."),
86
+ downloadMedia: import_zod.z.boolean().default(false).describe("Ask MCP Scraper to download page media when supported."),
87
+ maxCharacters: import_zod.z.number().int().min(500).max(5e5).default(8e4).describe("Maximum extracted content characters returned to the MCP caller.")
88
+ };
89
+ var ThorbitContentHarvestSerpInputSchema = {
90
+ query: import_zod.z.string().min(1).max(400).describe("Search query for MCP Scraper SERP/PAA harvest."),
91
+ location: import_zod.z.string().min(1).max(160).optional().describe("Optional search location, such as Denver, Colorado, United States."),
92
+ gl: import_zod.z.string().length(2).optional().describe("Optional Google country code, such as us."),
93
+ hl: import_zod.z.string().min(2).max(12).optional().describe("Optional Google interface language, such as en."),
94
+ maxQuestions: import_zod.z.number().int().min(1).max(150).default(30).describe("Maximum PAA questions when serpOnly is false."),
95
+ includeSerp: import_zod.z.boolean().default(true).describe("Include organic SERP results. Default true."),
96
+ serpOnly: import_zod.z.boolean().default(false).describe("Use fast SERP-only mode when PAA expansion is not needed.")
97
+ };
98
+ var ThorbitContentRedditResearchInputSchema = {
99
+ query: import_zod.z.string().min(1).max(400).describe("Topic, product, service, or pain point to research on Reddit."),
100
+ location: import_zod.z.string().min(1).max(160).optional().describe("Optional location to bias MCP Scraper SERP discovery."),
101
+ maxPosts: import_zod.z.number().int().min(1).max(10).default(5).describe("Maximum Reddit posts to read with MCP Scraper browser-agent."),
102
+ readWithBrowserAgent: import_zod.z.boolean().default(true).describe("Keep true. Reads Reddit candidates through MCP Scraper browser-agent."),
103
+ profile: import_zod.z.string().min(1).max(128).optional().describe("Optional MCP Scraper browser-agent saved profile name.")
104
+ };
105
+ var ThorbitOnPageStartAnalysisInputSchema = {
106
+ projectPublicId: import_zod.z.string().min(1).describe("Thorbit project public ID."),
107
+ keyword: import_zod.z.string().min(1).max(200).describe("Target keyword or query for on-page analysis."),
108
+ force: import_zod.z.boolean().default(false).describe("Force restart if an analysis is already running."),
109
+ source: import_zod.z.discriminatedUnion("mode", [
110
+ import_zod.z.object({ mode: import_zod.z.literal("keyword_only") }).strict(),
111
+ import_zod.z.object({
112
+ mode: import_zod.z.literal("inline_content"),
113
+ title: import_zod.z.string().min(1).max(255).optional(),
114
+ text: import_zod.z.string().min(20).max(5e5),
115
+ sourceUrl: import_zod.z.string().url().optional()
116
+ }).strict()
117
+ ]).default({ mode: "keyword_only" }).describe("Use keyword_only for research-only analysis or inline_content to score provided content.")
118
+ };
119
+ var ThorbitOnPageGetAnalysisInputSchema = {
120
+ analysisPublicId: import_zod.z.string().min(1).describe("On-page analysis public ID returned by thorbit_onpage_start_analysis.")
121
+ };
122
+
123
+ // src/content-onpage-mcp-server.ts
124
+ var VERSION = "0.1.0";
125
+ function readOnlyAnnotations(title, openWorldHint = true) {
126
+ return {
127
+ title,
128
+ readOnlyHint: true,
129
+ destructiveHint: false,
130
+ idempotentHint: true,
131
+ openWorldHint
132
+ };
133
+ }
134
+ function writeAnnotations(title, openWorldHint = false) {
135
+ return {
136
+ title,
137
+ readOnlyHint: false,
138
+ destructiveHint: false,
139
+ idempotentHint: false,
140
+ openWorldHint
141
+ };
142
+ }
143
+ function buildThorbitContentOnPageMcpServer(client) {
144
+ const server = new import_mcp.McpServer({ name: "thorbit-content-mcp", version: VERSION });
145
+ server.registerResource(
146
+ "analysis",
147
+ new import_mcp.ResourceTemplate("thorbit-content://analyses/{analysisPublicId}", { list: void 0 }),
148
+ {
149
+ title: "Thorbit On-Page Analysis",
150
+ description: "Status, score, signal counts, and summary for one Thorbit on-page analysis.",
151
+ mimeType: "application/json"
152
+ },
153
+ async (uri, variables) => {
154
+ const analysisPublicId = Array.isArray(variables.analysisPublicId) ? variables.analysisPublicId[0] : variables.analysisPublicId;
155
+ const envelope = await client.callTool("thorbit_onpage_get_analysis", { analysisPublicId: String(analysisPublicId) });
156
+ return { contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(envelope, null, 2) }] };
157
+ }
158
+ );
159
+ function registerTool(toolName, config) {
160
+ server.registerTool(toolName, config, async (input) => {
161
+ const envelope = await client.callTool(toolName, input);
162
+ return formatThorbitContentOnPageMcpToolResult(toolName, envelope);
163
+ });
164
+ }
165
+ registerTool("thorbit_content_extract_url", {
166
+ title: "Extract URL For Content Analysis",
167
+ description: "Extract a public URL through MCP Scraper only. Use this before content audits, source ingestion, outline planning, or on-page comparisons. Browser fallback is enabled by default for JS-heavy pages.",
168
+ inputSchema: ThorbitContentExtractUrlInputSchema,
169
+ annotations: readOnlyAnnotations("Extract URL For Content Analysis")
170
+ });
171
+ registerTool("thorbit_content_harvest_serp", {
172
+ title: "Harvest SERP And PAA Evidence",
173
+ description: "Harvest Google SERP/PAA evidence through MCP Scraper only. Returns PAA questions, organic results, forums, videos, AI Overview fields when available, and provider stats for source-backed content strategy.",
174
+ inputSchema: ThorbitContentHarvestSerpInputSchema,
175
+ annotations: readOnlyAnnotations("Harvest SERP And PAA Evidence")
176
+ });
177
+ registerTool("thorbit_content_reddit_research", {
178
+ title: "Research Reddit With MCP Scraper Browser Agent",
179
+ description: "Find Reddit candidates through MCP Scraper SERP harvest, then read selected posts through MCP Scraper browser-agent by default. Use for authentic audience language, objections, pain points, and questions. Do not use generic web scraping fallbacks for Reddit.",
180
+ inputSchema: ThorbitContentRedditResearchInputSchema,
181
+ annotations: readOnlyAnnotations("Research Reddit With Browser Agent")
182
+ });
183
+ registerTool("thorbit_onpage_start_analysis", {
184
+ title: "Start Thorbit On-Page Analysis",
185
+ description: "Start a Thorbit on-page analysis for a project keyword. Can run keyword-only analysis or score provided inline content. Hosted Thorbit dispatches the durable analysis workflow and meters usage against the MCP API key.",
186
+ inputSchema: ThorbitOnPageStartAnalysisInputSchema,
187
+ annotations: writeAnnotations("Start Thorbit On-Page Analysis")
188
+ });
189
+ registerTool("thorbit_onpage_get_analysis", {
190
+ title: "Read Thorbit On-Page Analysis",
191
+ description: "Read persisted on-page analysis status, score, signal counts, and content report summary by analysis public ID. Use after thorbit_onpage_start_analysis to poll durable results.",
192
+ inputSchema: ThorbitOnPageGetAnalysisInputSchema,
193
+ annotations: readOnlyAnnotations("Read Thorbit On-Page Analysis", false)
194
+ });
195
+ return server;
196
+ }
197
+
198
+ // bin/thorbit-content-mcp.ts
199
+ async function main() {
200
+ const env = resolveThorbitContentOnPageMcpEnv();
201
+ const client = new ThorbitContentOnPageApiClient(env);
202
+ const server = buildThorbitContentOnPageMcpServer(client);
203
+ await server.connect(new import_stdio.StdioServerTransport());
204
+ }
205
+ main().catch((error) => {
206
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}
207
+ `);
208
+ process.exit(1);
209
+ });
210
+ //# sourceMappingURL=thorbit-content-mcp.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../bin/thorbit-content-mcp.ts","../../src/content-onpage-api-client.ts","../../src/content-onpage-mcp-env.ts","../../src/content-onpage-mcp-server.ts","../../src/content-onpage-mcp-response-formatters.ts","../../src/content-onpage-mcp-tool-schemas.ts"],"sourcesContent":["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { ThorbitContentOnPageApiClient } from '../src/content-onpage-api-client.js'\nimport { resolveThorbitContentOnPageMcpEnv } from '../src/content-onpage-mcp-env.js'\nimport { buildThorbitContentOnPageMcpServer } from '../src/content-onpage-mcp-server.js'\n\nasync function main() {\n const env = resolveThorbitContentOnPageMcpEnv()\n const client = new ThorbitContentOnPageApiClient(env)\n const server = buildThorbitContentOnPageMcpServer(client)\n await server.connect(new StdioServerTransport())\n}\n\nmain().catch(error => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`)\n process.exit(1)\n})\n","import type { ThorbitContentOnPageMcpToolName } from './content-onpage-mcp-tool-names.js'\n\nexport type ThorbitContentOnPageMcpEnvelope =\n | {\n ok: true\n result: unknown\n warnings?: string[]\n requestId: string\n usage?: Record<string, unknown>\n }\n | {\n ok: false\n error: {\n code: string\n message: string\n details?: unknown\n }\n requestId: string\n }\n\nexport class ThorbitContentOnPageApiClient {\n private readonly baseUrl: string\n private readonly apiKey: string\n\n constructor(options: { baseUrl: string; apiKey: string }) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, '')\n this.apiKey = options.apiKey\n }\n\n async callTool(toolName: ThorbitContentOnPageMcpToolName, input: unknown): Promise<ThorbitContentOnPageMcpEnvelope> {\n const response = await fetch(`${this.baseUrl}/api/v1/mcp/content-onpage/${toolName}`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(input ?? {}),\n })\n\n const body = await response.json().catch(() => null) as ThorbitContentOnPageMcpEnvelope | null\n if (body && typeof body === 'object' && 'ok' in body) return body\n\n return {\n ok: false,\n requestId: 'unavailable',\n error: {\n code: response.ok ? 'invalid_response' : `http_${response.status}`,\n message: response.ok ? 'Thorbit returned an invalid MCP response' : `Thorbit API request failed with HTTP ${response.status}`,\n },\n }\n }\n}\n","import { readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nexport type ThorbitContentOnPageMcpEnv = {\n apiKey: string\n baseUrl: string\n}\n\nfunction readApiKeyFile(): string | undefined {\n const explicitPath = process.env.THORBIT_CONTENT_MCP_KEY_PATH?.trim()\n const paths = [explicitPath, join(homedir(), '.thorbit-content-mcp-key')].filter(Boolean) as string[]\n for (const path of paths) {\n try {\n const value = readFileSync(path, 'utf8').trim()\n if (value) return value\n } catch {\n continue\n }\n }\n return undefined\n}\n\nexport function resolveThorbitContentOnPageMcpEnv(): ThorbitContentOnPageMcpEnv {\n const apiKey = (\n process.env.THORBIT_API_KEY ||\n process.env.THORBIT_MCP_API_KEY ||\n process.env.THORBIT_CONTENT_MCP_API_KEY ||\n readApiKeyFile()\n )?.trim()\n\n if (!apiKey) {\n throw new Error('THORBIT_API_KEY, THORBIT_MCP_API_KEY, or ~/.thorbit-content-mcp-key is required')\n }\n\n return {\n apiKey,\n baseUrl: (process.env.THORBIT_BASE_URL || process.env.THORBIT_CONTENT_MCP_BASE_URL || 'https://thorbit.ai').trim(),\n }\n}\n","import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'\nimport type { ZodRawShape } from 'zod'\nimport type { ThorbitContentOnPageApiClient } from './content-onpage-api-client.js'\nimport { formatThorbitContentOnPageMcpToolResult } from './content-onpage-mcp-response-formatters.js'\nimport {\n ThorbitContentExtractUrlInputSchema,\n ThorbitContentHarvestSerpInputSchema,\n ThorbitContentRedditResearchInputSchema,\n ThorbitOnPageGetAnalysisInputSchema,\n ThorbitOnPageStartAnalysisInputSchema,\n} from './content-onpage-mcp-tool-schemas.js'\nimport type { ThorbitContentOnPageMcpToolName } from './content-onpage-mcp-tool-names.js'\n\nconst VERSION = '0.1.0'\n\ntype ThorbitContentOnPageToolConfig<InputArgs extends ZodRawShape> = {\n title: string\n description: string\n inputSchema: InputArgs\n annotations: ToolAnnotations\n}\n\nfunction readOnlyAnnotations(title: string, openWorldHint = true) {\n return {\n title,\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint,\n }\n}\n\nfunction writeAnnotations(title: string, openWorldHint = false) {\n return {\n title,\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint,\n }\n}\n\nexport function buildThorbitContentOnPageMcpServer(client: ThorbitContentOnPageApiClient): McpServer {\n const server = new McpServer({ name: 'thorbit-content-mcp', version: VERSION })\n\n server.registerResource(\n 'analysis',\n new ResourceTemplate('thorbit-content://analyses/{analysisPublicId}', { list: undefined }),\n {\n title: 'Thorbit On-Page Analysis',\n description: 'Status, score, signal counts, and summary for one Thorbit on-page analysis.',\n mimeType: 'application/json',\n },\n async (uri, variables) => {\n const analysisPublicId = Array.isArray(variables.analysisPublicId)\n ? variables.analysisPublicId[0]\n : variables.analysisPublicId\n const envelope = await client.callTool('thorbit_onpage_get_analysis', { analysisPublicId: String(analysisPublicId) })\n return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(envelope, null, 2) }] }\n },\n )\n\n function registerTool<T extends ThorbitContentOnPageMcpToolName>(\n toolName: T,\n config: ThorbitContentOnPageToolConfig<ZodRawShape>,\n ): void {\n server.registerTool(toolName, config, async (input) => {\n const envelope = await client.callTool(toolName, input)\n return formatThorbitContentOnPageMcpToolResult(toolName, envelope)\n })\n }\n\n registerTool('thorbit_content_extract_url', {\n title: 'Extract URL For Content Analysis',\n description: 'Extract a public URL through MCP Scraper only. Use this before content audits, source ingestion, outline planning, or on-page comparisons. Browser fallback is enabled by default for JS-heavy pages.',\n inputSchema: ThorbitContentExtractUrlInputSchema,\n annotations: readOnlyAnnotations('Extract URL For Content Analysis'),\n })\n\n registerTool('thorbit_content_harvest_serp', {\n title: 'Harvest SERP And PAA Evidence',\n description: 'Harvest Google SERP/PAA evidence through MCP Scraper only. Returns PAA questions, organic results, forums, videos, AI Overview fields when available, and provider stats for source-backed content strategy.',\n inputSchema: ThorbitContentHarvestSerpInputSchema,\n annotations: readOnlyAnnotations('Harvest SERP And PAA Evidence'),\n })\n\n registerTool('thorbit_content_reddit_research', {\n title: 'Research Reddit With MCP Scraper Browser Agent',\n description: 'Find Reddit candidates through MCP Scraper SERP harvest, then read selected posts through MCP Scraper browser-agent by default. Use for authentic audience language, objections, pain points, and questions. Do not use generic web scraping fallbacks for Reddit.',\n inputSchema: ThorbitContentRedditResearchInputSchema,\n annotations: readOnlyAnnotations('Research Reddit With Browser Agent'),\n })\n\n registerTool('thorbit_onpage_start_analysis', {\n title: 'Start Thorbit On-Page Analysis',\n description: 'Start a Thorbit on-page analysis for a project keyword. Can run keyword-only analysis or score provided inline content. Hosted Thorbit dispatches the durable analysis workflow and meters usage against the MCP API key.',\n inputSchema: ThorbitOnPageStartAnalysisInputSchema,\n annotations: writeAnnotations('Start Thorbit On-Page Analysis'),\n })\n\n registerTool('thorbit_onpage_get_analysis', {\n title: 'Read Thorbit On-Page Analysis',\n description: 'Read persisted on-page analysis status, score, signal counts, and content report summary by analysis public ID. Use after thorbit_onpage_start_analysis to poll durable results.',\n inputSchema: ThorbitOnPageGetAnalysisInputSchema,\n annotations: readOnlyAnnotations('Read Thorbit On-Page Analysis', false),\n })\n\n return server\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport type { ThorbitContentOnPageMcpEnvelope } from './content-onpage-api-client.js'\nimport type { ThorbitContentOnPageMcpToolName } from './content-onpage-mcp-tool-names.js'\n\nexport function formatThorbitContentOnPageMcpToolResult(\n toolName: ThorbitContentOnPageMcpToolName,\n envelope: ThorbitContentOnPageMcpEnvelope,\n): CallToolResult {\n const text = JSON.stringify({\n toolName,\n ...envelope,\n }, null, 2)\n return {\n content: [{ type: 'text', text }],\n isError: !envelope.ok,\n }\n}\n","import { z } from 'zod'\n\nexport const ThorbitContentExtractUrlInputSchema = {\n url: z.string().url().describe('Public URL to extract through MCP Scraper.'),\n browserFallback: z.boolean().default(true).describe('Use MCP Scraper browser fallback for JS-heavy pages. Default true.'),\n extractBranding: z.boolean().default(false).describe('Ask MCP Scraper to extract brand colors, fonts, logo, and favicon when supported.'),\n downloadMedia: z.boolean().default(false).describe('Ask MCP Scraper to download page media when supported.'),\n maxCharacters: z.number().int().min(500).max(500000).default(80000).describe('Maximum extracted content characters returned to the MCP caller.'),\n}\n\nexport const ThorbitContentHarvestSerpInputSchema = {\n query: z.string().min(1).max(400).describe('Search query for MCP Scraper SERP/PAA harvest.'),\n location: z.string().min(1).max(160).optional().describe('Optional search location, such as Denver, Colorado, United States.'),\n gl: z.string().length(2).optional().describe('Optional Google country code, such as us.'),\n hl: z.string().min(2).max(12).optional().describe('Optional Google interface language, such as en.'),\n maxQuestions: z.number().int().min(1).max(150).default(30).describe('Maximum PAA questions when serpOnly is false.'),\n includeSerp: z.boolean().default(true).describe('Include organic SERP results. Default true.'),\n serpOnly: z.boolean().default(false).describe('Use fast SERP-only mode when PAA expansion is not needed.'),\n}\n\nexport const ThorbitContentRedditResearchInputSchema = {\n query: z.string().min(1).max(400).describe('Topic, product, service, or pain point to research on Reddit.'),\n location: z.string().min(1).max(160).optional().describe('Optional location to bias MCP Scraper SERP discovery.'),\n maxPosts: z.number().int().min(1).max(10).default(5).describe('Maximum Reddit posts to read with MCP Scraper browser-agent.'),\n readWithBrowserAgent: z.boolean().default(true).describe('Keep true. Reads Reddit candidates through MCP Scraper browser-agent.'),\n profile: z.string().min(1).max(128).optional().describe('Optional MCP Scraper browser-agent saved profile name.'),\n}\n\nexport const ThorbitOnPageStartAnalysisInputSchema = {\n projectPublicId: z.string().min(1).describe('Thorbit project public ID.'),\n keyword: z.string().min(1).max(200).describe('Target keyword or query for on-page analysis.'),\n force: z.boolean().default(false).describe('Force restart if an analysis is already running.'),\n source: z.discriminatedUnion('mode', [\n z.object({ mode: z.literal('keyword_only') }).strict(),\n z.object({\n mode: z.literal('inline_content'),\n title: z.string().min(1).max(255).optional(),\n text: z.string().min(20).max(500000),\n sourceUrl: z.string().url().optional(),\n }).strict(),\n ]).default({ mode: 'keyword_only' }).describe('Use keyword_only for research-only analysis or inline_content to score provided content.'),\n}\n\nexport const ThorbitOnPageGetAnalysisInputSchema = {\n analysisPublicId: z.string().min(1).describe('On-page analysis public ID returned by thorbit_onpage_start_analysis.'),\n}\n\nexport const ThorbitContentOnPageMcpToolInputSchemas = {\n thorbit_content_extract_url: ThorbitContentExtractUrlInputSchema,\n thorbit_content_harvest_serp: ThorbitContentHarvestSerpInputSchema,\n thorbit_content_reddit_research: ThorbitContentRedditResearchInputSchema,\n thorbit_onpage_start_analysis: ThorbitOnPageStartAnalysisInputSchema,\n thorbit_onpage_get_analysis: ThorbitOnPageGetAnalysisInputSchema,\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }