@vespermcp/setup 3.0.1 → 3.1.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 +2 -0
- package/package.json +1 -1
- package/wizard.js +142 -13
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Interactive setup for the Vesper MCP stack: link your [getvesper.dev](https://getvesper.dev) account, store your API key locally, and auto-write MCP config for Cursor, Claude, VS Code, Codex, and Gemini CLI.
|
|
4
4
|
|
|
5
|
+
The wizard also asks whether you want **web URL extraction** (`vesper.extract_web`). If you opt in and have **Docker**, it runs **`docker pull`** / **`docker run`** for the official [Crawl4AI](https://hub.docker.com/r/unclecode/crawl4ai) image and sets `VESPER_CRAWL4AI_URL` in your MCP env — no separate web search required.
|
|
6
|
+
|
|
5
7
|
## Run
|
|
6
8
|
|
|
7
9
|
```bash
|
package/package.json
CHANGED
package/wizard.js
CHANGED
|
@@ -388,13 +388,18 @@ function buildMcpServerEntry() {
|
|
|
388
388
|
const npxCmd = IS_WIN ? 'npx.cmd' : 'npx';
|
|
389
389
|
const state = readToml(CONFIG_TOML);
|
|
390
390
|
const apiKey = String(state.api_key || '').trim();
|
|
391
|
+
const crawl4aiUrl = String(state.crawl4ai_url || '').trim();
|
|
392
|
+
const env = {
|
|
393
|
+
VESPER_API_URL: getMcpVesperApiUrl(),
|
|
394
|
+
VESPER_API_KEY: apiKey || 'your-key-from-getvesper.dev',
|
|
395
|
+
};
|
|
396
|
+
if (crawl4aiUrl) {
|
|
397
|
+
env.VESPER_CRAWL4AI_URL = crawl4aiUrl;
|
|
398
|
+
}
|
|
391
399
|
return {
|
|
392
400
|
command: npxCmd,
|
|
393
401
|
args: ['-y', '-p', '@vespermcp/mcp-server@latest', 'vespermcp'],
|
|
394
|
-
env
|
|
395
|
-
VESPER_API_URL: getMcpVesperApiUrl(),
|
|
396
|
-
VESPER_API_KEY: apiKey || 'your-key-from-getvesper.dev',
|
|
397
|
-
},
|
|
402
|
+
env,
|
|
398
403
|
};
|
|
399
404
|
}
|
|
400
405
|
|
|
@@ -405,13 +410,17 @@ function escapeTomlDoubleQuoted(value) {
|
|
|
405
410
|
function upsertTomlMcpVesperBlock(content, serverEntry) {
|
|
406
411
|
const key = escapeTomlDoubleQuoted(serverEntry.env.VESPER_API_KEY);
|
|
407
412
|
const url = escapeTomlDoubleQuoted(serverEntry.env.VESPER_API_URL);
|
|
413
|
+
const crawlLine = serverEntry.env.VESPER_CRAWL4AI_URL
|
|
414
|
+
? `VESPER_CRAWL4AI_URL = "${escapeTomlDoubleQuoted(serverEntry.env.VESPER_CRAWL4AI_URL)}"\n`
|
|
415
|
+
: '';
|
|
408
416
|
const block =
|
|
409
417
|
`[mcp_servers.vesper]\n` +
|
|
410
418
|
`command = "${serverEntry.command}"\n` +
|
|
411
419
|
`args = [${serverEntry.args.map((a) => `"${a}"`).join(', ')}]\n\n` +
|
|
412
420
|
`[mcp_servers.vesper.env]\n` +
|
|
413
421
|
`VESPER_API_KEY = "${key}"\n` +
|
|
414
|
-
`VESPER_API_URL = "${url}"\n
|
|
422
|
+
`VESPER_API_URL = "${url}"\n` +
|
|
423
|
+
crawlLine;
|
|
415
424
|
const re = /\[mcp_servers\.vesper\][\s\S]*?(?=\n\[|$)/;
|
|
416
425
|
if (re.test(content)) {
|
|
417
426
|
return content.replace(re, block.trim() + '\n');
|
|
@@ -512,6 +521,106 @@ async function checkServerHealth() {
|
|
|
512
521
|
}
|
|
513
522
|
}
|
|
514
523
|
|
|
524
|
+
const CRAWL4AI_CONTAINER = 'vesper-crawl4ai';
|
|
525
|
+
const CRAWL4AI_DEFAULT_URL = 'http://127.0.0.1:11235';
|
|
526
|
+
|
|
527
|
+
function printManualCrawl4AiInstructions() {
|
|
528
|
+
console.log(`\n ${dim('Run these in a terminal when Docker is available:')}`);
|
|
529
|
+
console.log(` ${cyan('docker pull unclecode/crawl4ai')}`);
|
|
530
|
+
console.log(
|
|
531
|
+
` ${cyan(
|
|
532
|
+
`docker run -d --name ${CRAWL4AI_CONTAINER} -p 11235:11235 --shm-size=1g --restart unless-stopped unclecode/crawl4ai`,
|
|
533
|
+
)}`,
|
|
534
|
+
);
|
|
535
|
+
console.log(` ${dim('Then set in MCP env:')} ${cyan(`VESPER_CRAWL4AI_URL=${CRAWL4AI_DEFAULT_URL}`)}`);
|
|
536
|
+
console.log(` ${dim('Docs:')} https://github.com/vesper/mcp-server/blob/main/docs/web-extraction.md\n`);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function dockerCliWorks() {
|
|
540
|
+
const r = spawnSync('docker', ['info'], { encoding: 'utf8', timeout: 20000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
541
|
+
return r.status === 0;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function refreshMcpConfigsForAgents() {
|
|
545
|
+
const agents = getAllAgentConfigs();
|
|
546
|
+
for (const agent of agents) {
|
|
547
|
+
const dirExists = fs.existsSync(path.dirname(agent.path));
|
|
548
|
+
const fileExists = fs.existsSync(agent.path);
|
|
549
|
+
if (fileExists || dirExists) {
|
|
550
|
+
installMcpToAgent(agent);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
async function runCrawl4AiDockerSetup() {
|
|
556
|
+
if (!dockerCliWorks()) {
|
|
557
|
+
console.log(` ${yellow('!')} ${yellow('Docker CLI did not respond (is Docker Desktop / the daemon running?)')}`);
|
|
558
|
+
printManualCrawl4AiInstructions();
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
console.log(`\n ${dim('Pulling image unclecode/crawl4ai (first run may take a few minutes)...')}`);
|
|
563
|
+
const pull = spawnSync('docker', ['pull', 'unclecode/crawl4ai'], {
|
|
564
|
+
stdio: 'inherit',
|
|
565
|
+
timeout: 600000,
|
|
566
|
+
});
|
|
567
|
+
if (pull.error) {
|
|
568
|
+
console.log(` ${red('✗')} docker pull failed: ${pull.error.message || pull.error}`);
|
|
569
|
+
printManualCrawl4AiInstructions();
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
if (pull.status !== 0) {
|
|
573
|
+
console.log(` ${red('✗')} docker pull exited with status ${pull.status}`);
|
|
574
|
+
printManualCrawl4AiInstructions();
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const inspect = spawnSync('docker', ['inspect', CRAWL4AI_CONTAINER], {
|
|
579
|
+
encoding: 'utf8',
|
|
580
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
581
|
+
});
|
|
582
|
+
if (inspect.status === 0) {
|
|
583
|
+
console.log(` ${dim('Container')} ${cyan(CRAWL4AI_CONTAINER)} ${dim('exists — starting it...')}`);
|
|
584
|
+
const start = spawnSync('docker', ['start', CRAWL4AI_CONTAINER], { stdio: 'inherit', timeout: 120000 });
|
|
585
|
+
if (start.status !== 0) {
|
|
586
|
+
console.log(` ${yellow('!')} docker start failed — try: ${cyan(`docker start ${CRAWL4AI_CONTAINER}`)}`);
|
|
587
|
+
}
|
|
588
|
+
} else {
|
|
589
|
+
console.log(` ${dim('Creating container')} ${cyan(CRAWL4AI_CONTAINER)}...`);
|
|
590
|
+
const run = spawnSync(
|
|
591
|
+
'docker',
|
|
592
|
+
[
|
|
593
|
+
'run',
|
|
594
|
+
'-d',
|
|
595
|
+
'--name',
|
|
596
|
+
CRAWL4AI_CONTAINER,
|
|
597
|
+
'-p',
|
|
598
|
+
'11235:11235',
|
|
599
|
+
'--shm-size',
|
|
600
|
+
'1g',
|
|
601
|
+
'--restart',
|
|
602
|
+
'unless-stopped',
|
|
603
|
+
'unclecode/crawl4ai',
|
|
604
|
+
],
|
|
605
|
+
{ stdio: 'inherit', timeout: 180000 },
|
|
606
|
+
);
|
|
607
|
+
if (run.status !== 0) {
|
|
608
|
+
console.log(` ${red('✗')} docker run failed`);
|
|
609
|
+
printManualCrawl4AiInstructions();
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const merged = { ...readToml(CONFIG_TOML), crawl4ai_url: CRAWL4AI_DEFAULT_URL };
|
|
615
|
+
writeToml(CONFIG_TOML, merged);
|
|
616
|
+
console.log(` ${green('✓')} Saved ${dim('crawl4ai_url')} = ${cyan(CRAWL4AI_DEFAULT_URL)} ${dim('→')} ${dim(CONFIG_TOML)}`);
|
|
617
|
+
|
|
618
|
+
refreshMcpConfigsForAgents();
|
|
619
|
+
console.log(` ${green('✓')} Updated MCP client configs with ${cyan('VESPER_CRAWL4AI_URL')}`);
|
|
620
|
+
console.log(` ${dim('Crawl4AI API (after a few seconds):')} ${cyan(`${CRAWL4AI_DEFAULT_URL}/playground`)}`);
|
|
621
|
+
return true;
|
|
622
|
+
}
|
|
623
|
+
|
|
515
624
|
// ── Main Wizard ──────────────────────────────────────────────
|
|
516
625
|
async function main() {
|
|
517
626
|
if (!isInteractiveTerminal()) {
|
|
@@ -526,7 +635,7 @@ async function main() {
|
|
|
526
635
|
console.log(` ${green('→')} Setting up Vesper on ${bold(os.hostname())}\n`);
|
|
527
636
|
|
|
528
637
|
// ─── Step 1: Create directories ────────────────────────────
|
|
529
|
-
process.stdout.write(` ${dim('[')}${cyan('1/
|
|
638
|
+
process.stdout.write(` ${dim('[')}${cyan('1/7')}${dim(']')} Creating local directories...`);
|
|
530
639
|
ensureDir(VESPER_DIR);
|
|
531
640
|
ensureDir(DATA_DIR);
|
|
532
641
|
ensureDir(path.join(DATA_DIR, 'raw'));
|
|
@@ -535,7 +644,7 @@ async function main() {
|
|
|
535
644
|
console.log(` ${green('✓')}`);
|
|
536
645
|
|
|
537
646
|
// ─── Step 2: Authenticate (device flow or local key) ──────
|
|
538
|
-
console.log(`\n ${dim('[')}${cyan('2/
|
|
647
|
+
console.log(`\n ${dim('[')}${cyan('2/7')}${dim(']')} Authentication`);
|
|
539
648
|
|
|
540
649
|
const existing = readToml(CONFIG_TOML);
|
|
541
650
|
let localKey = existing.api_key || '';
|
|
@@ -564,7 +673,7 @@ async function main() {
|
|
|
564
673
|
console.log(` ${dim('Key:')} ${dim(localKey.slice(0, 24) + '...')} ${dim('→')} ${dim(CONFIG_TOML)}`);
|
|
565
674
|
|
|
566
675
|
// ─── Step 3: Local vault initialization ────────────────────
|
|
567
|
-
process.stdout.write(`\n ${dim('[')}${cyan('3/
|
|
676
|
+
process.stdout.write(`\n ${dim('[')}${cyan('3/7')}${dim(']')} Initializing local credentials vault...`);
|
|
568
677
|
const vaultData = readToml(CONFIG_TOML);
|
|
569
678
|
if (!vaultData.auth_mode) vaultData.auth_mode = 'local_unified';
|
|
570
679
|
writeToml(CONFIG_TOML, vaultData);
|
|
@@ -572,7 +681,7 @@ async function main() {
|
|
|
572
681
|
console.log(` ${dim('Mode:')} ${dim(vaultData.auth_mode === 'cloud' ? 'cloud (linked to Vesper account)' : 'single local Vesper key (no external keys required)')}`);
|
|
573
682
|
|
|
574
683
|
// ─── Step 4: Verify @vespermcp/mcp-server command ───────────
|
|
575
|
-
console.log(`\n ${dim('[')}${cyan('4/
|
|
684
|
+
console.log(`\n ${dim('[')}${cyan('4/7')}${dim(']')} Verifying Vesper MCP server command...`);
|
|
576
685
|
try {
|
|
577
686
|
const npmCmd = IS_WIN ? 'npx.cmd' : 'npx';
|
|
578
687
|
const verify = spawnSync(npmCmd, ['-y', '-p', '@vespermcp/mcp-server@latest', 'vespermcp', '--version'], {
|
|
@@ -590,7 +699,7 @@ async function main() {
|
|
|
590
699
|
}
|
|
591
700
|
|
|
592
701
|
// ─── Step 5: Auto-configure all detected IDEs ──────────────
|
|
593
|
-
process.stdout.write(`\n ${dim('[')}${cyan('5/
|
|
702
|
+
process.stdout.write(`\n ${dim('[')}${cyan('5/7')}${dim(']')} Configuring coding agents...`);
|
|
594
703
|
const agents = getAllAgentConfigs();
|
|
595
704
|
const configuredAgents = [];
|
|
596
705
|
const skippedAgents = [];
|
|
@@ -616,8 +725,27 @@ async function main() {
|
|
|
616
725
|
console.log(` └───────────────────────────────────────────────┘`);
|
|
617
726
|
}
|
|
618
727
|
|
|
619
|
-
// ─── Step 6:
|
|
620
|
-
console.log(`\n ${dim('[')}${cyan('6/
|
|
728
|
+
// ─── Step 6: Web extraction (Crawl4AI + Docker) ─────────────
|
|
729
|
+
console.log(`\n ${dim('[')}${cyan('6/7')}${dim(']')} Web extraction (optional)`);
|
|
730
|
+
console.log(
|
|
731
|
+
` ${dim('Tools like')} ${cyan('vesper.extract_web')} ${dim('use Crawl4AI. The easiest way is Docker on your machine (no extra API keys).')}\n`,
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
const wantWebExtraction = await askYesNo(`${cyan('→')} Enable web URL extraction (Crawl4AI + Docker)?`);
|
|
735
|
+
if (wantWebExtraction) {
|
|
736
|
+
const hasDocker = await askYesNo(`${cyan('→')} Do you have Docker installed and running (Docker Desktop / engine)?`);
|
|
737
|
+
if (hasDocker) {
|
|
738
|
+
await runCrawl4AiDockerSetup();
|
|
739
|
+
} else {
|
|
740
|
+
console.log(`\n ${dim('Install Docker from https://docs.docker.com/get-docker/ then run the commands below, or re-run this wizard.')}`);
|
|
741
|
+
printManualCrawl4AiInstructions();
|
|
742
|
+
}
|
|
743
|
+
} else {
|
|
744
|
+
console.log(` ${dim('Skipped. To enable later, see docs/web-extraction.md')}`);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// ─── Step 7: Verify ────────────────────────────────────────
|
|
748
|
+
console.log(`\n ${dim('[')}${cyan('7/7')}${dim(']')} Verifying installation...`);
|
|
621
749
|
|
|
622
750
|
const dbExists = fs.existsSync(path.join(DATA_DIR, 'metadata.db'));
|
|
623
751
|
const vecExists = fs.existsSync(path.join(DATA_DIR, 'vectors.json')) || fs.existsSync(path.join(DATA_DIR, 'vectors.bin'));
|
|
@@ -649,7 +777,8 @@ ${dim('════════════════════════
|
|
|
649
777
|
${dim('1.')} ${isCloud ? 'Linked to your Vesper cloud account' : 'Generated a local API key (never leaves your machine)'}
|
|
650
778
|
${dim('2.')} Initialized local credentials vault
|
|
651
779
|
${dim('3.')} Auto-configured MCP for ${configuredAgents.length > 0 ? configuredAgents.join(', ') : 'detected agents'}
|
|
652
|
-
${dim('4.')}
|
|
780
|
+
${dim('4.')} Optional: Crawl4AI for web extraction (${dim('vesper.extract_web')}) — Docker step if you chose it
|
|
781
|
+
${dim('5.')} Vesper server ready on stdio transport
|
|
653
782
|
|
|
654
783
|
${dim('─────────────────────────────────────────────────')}
|
|
655
784
|
|