@vespermcp/mcp-server 1.1.3 → 1.2.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/README.md +34 -0
- package/build/config/secure-keys.js +51 -0
- package/build/config/user-config.js +48 -0
- package/build/fusion/engine.js +69 -0
- package/build/index.js +813 -25
- package/build/ingestion/hf-downloader.js +12 -3
- package/build/ingestion/ingestor.js +33 -9
- package/build/metadata/kaggle-source.js +70 -0
- package/build/metadata/scraper.js +34 -10
- package/build/python/config.py +259 -0
- package/build/python/export_engine.py +148 -52
- package/build/python/fusion_engine.py +368 -0
- package/build/python/kaggle_engine.py +204 -0
- package/build/python/row_count.py +54 -0
- package/build/python/test_fusion_engine.py +89 -0
- package/build/scripts/build-index.js +5 -5
- package/build/search/jit-orchestrator.js +74 -14
- package/package.json +8 -2
- package/scripts/refresh-index.cjs +87 -0
- package/src/python/__pycache__/export_engine.cpython-312.pyc +0 -0
- package/src/python/__pycache__/fusion_engine.cpython-312.pyc +0 -0
- package/src/python/config.py +259 -0
- package/src/python/export_engine.py +148 -52
- package/src/python/fusion_engine.py +368 -0
- package/src/python/kaggle_engine.py +204 -0
- package/src/python/row_count.py +54 -0
- package/src/python/test_fusion_engine.py +89 -0
package/README.md
CHANGED
|
@@ -89,6 +89,40 @@ Vesper attempts to auto-configure itself! Restart Claude and check. If not:
|
|
|
89
89
|
- `KAGGLE_USERNAME` & `KAGGLE_KEY`: For Kaggle dataset access
|
|
90
90
|
- `HF_TOKEN`: For private HuggingFace datasets
|
|
91
91
|
|
|
92
|
+
### Optional Kaggle Setup (Not Required)
|
|
93
|
+
|
|
94
|
+
Core Vesper works without any API keys. Keys are only needed when you explicitly use Kaggle or gated Hugging Face.
|
|
95
|
+
|
|
96
|
+
Install optional Kaggle client only if you need Kaggle source access:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pip install kaggle
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
vespermcp config keys
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The setup wizard supports skip and stores keys securely via OS keyring when available,
|
|
107
|
+
with fallback to `~/.vesper/config.toml`.
|
|
108
|
+
|
|
109
|
+
or use Kaggle's native file:
|
|
110
|
+
|
|
111
|
+
- `~/.kaggle/kaggle.json`
|
|
112
|
+
|
|
113
|
+
If credentials are missing and you run Kaggle commands, Vesper shows:
|
|
114
|
+
|
|
115
|
+
`Kaggle support requires API key. Run 'vespermcp config keys' (30 seconds).`
|
|
116
|
+
|
|
117
|
+
### CLI Examples
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
vespermcp discover --source kaggle "credit risk" --limit 10
|
|
121
|
+
vespermcp discover --source huggingface "credit risk" --limit 10
|
|
122
|
+
vespermcp download kaggle username/dataset-name
|
|
123
|
+
vespermcp download kaggle https://www.kaggle.com/datasets/username/dataset-name --target-dir ./data
|
|
124
|
+
```
|
|
125
|
+
|
|
92
126
|
## 🚀 Quick Start
|
|
93
127
|
|
|
94
128
|
After installation and configuration, restart your AI assistant and try:
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { spawnSync } from "child_process";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
export class SecureKeysManager {
|
|
5
|
+
pythonPath = "python";
|
|
6
|
+
scriptPath;
|
|
7
|
+
constructor(buildDir = process.cwd()) {
|
|
8
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || buildDir;
|
|
9
|
+
const dataRoot = path.join(homeDir, ".vesper");
|
|
10
|
+
const scriptPath0 = path.resolve(dataRoot, "python", "config.py");
|
|
11
|
+
const scriptPath1 = path.resolve(buildDir, "python", "config.py");
|
|
12
|
+
const scriptPath2 = path.resolve(buildDir, "..", "src", "python", "config.py");
|
|
13
|
+
if (fs.existsSync(scriptPath0))
|
|
14
|
+
this.scriptPath = scriptPath0;
|
|
15
|
+
else if (fs.existsSync(scriptPath1))
|
|
16
|
+
this.scriptPath = scriptPath1;
|
|
17
|
+
else if (fs.existsSync(scriptPath2))
|
|
18
|
+
this.scriptPath = scriptPath2;
|
|
19
|
+
else
|
|
20
|
+
this.scriptPath = scriptPath0;
|
|
21
|
+
if (process.platform === "win32")
|
|
22
|
+
this.pythonPath = "py";
|
|
23
|
+
}
|
|
24
|
+
getAll() {
|
|
25
|
+
const result = spawnSync(this.pythonPath, [this.scriptPath, "all"], { encoding: "utf-8" });
|
|
26
|
+
if (result.status !== 0)
|
|
27
|
+
return {};
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(result.stdout || "{}");
|
|
30
|
+
if (!parsed.ok)
|
|
31
|
+
return {};
|
|
32
|
+
return (parsed.data || {});
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
set(name, value) {
|
|
39
|
+
const result = spawnSync(this.pythonPath, [this.scriptPath, "set", name, value], { encoding: "utf-8" });
|
|
40
|
+
if (result.status !== 0) {
|
|
41
|
+
return { ok: false, error: result.stderr || "Failed to store key" };
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(result.stdout || "{}");
|
|
45
|
+
return { ok: !!parsed.ok, method: parsed.method, error: parsed.error };
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { ok: false, error: "Invalid response from config helper" };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
export class UserConfigStore {
|
|
5
|
+
configPath;
|
|
6
|
+
constructor(dataRoot) {
|
|
7
|
+
const homeDir = os.homedir() || process.env.HOME || process.env.USERPROFILE || process.cwd();
|
|
8
|
+
const root = dataRoot || path.join(homeDir, ".vesper");
|
|
9
|
+
if (!fs.existsSync(root)) {
|
|
10
|
+
fs.mkdirSync(root, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
this.configPath = path.join(root, "config.json");
|
|
13
|
+
}
|
|
14
|
+
getPath() {
|
|
15
|
+
return this.configPath;
|
|
16
|
+
}
|
|
17
|
+
load() {
|
|
18
|
+
if (!fs.existsSync(this.configPath))
|
|
19
|
+
return {};
|
|
20
|
+
try {
|
|
21
|
+
const raw = fs.readFileSync(this.configPath, "utf8");
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
return parsed || {};
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
save(config) {
|
|
30
|
+
const parent = path.dirname(this.configPath);
|
|
31
|
+
if (!fs.existsSync(parent))
|
|
32
|
+
fs.mkdirSync(parent, { recursive: true });
|
|
33
|
+
fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2), "utf8");
|
|
34
|
+
}
|
|
35
|
+
setKaggleCredentials(username, key) {
|
|
36
|
+
const cfg = this.load();
|
|
37
|
+
cfg.kaggle = { username, key };
|
|
38
|
+
this.save(cfg);
|
|
39
|
+
}
|
|
40
|
+
getKaggleCredentials() {
|
|
41
|
+
const cfg = this.load();
|
|
42
|
+
return cfg.kaggle || {};
|
|
43
|
+
}
|
|
44
|
+
hasKaggleCredentials() {
|
|
45
|
+
const creds = this.getKaggleCredentials();
|
|
46
|
+
return !!(creds.username && creds.key);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
export class DataFusionEngine {
|
|
5
|
+
pythonPath = "python";
|
|
6
|
+
scriptPath;
|
|
7
|
+
constructor(buildDir = process.cwd()) {
|
|
8
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || buildDir;
|
|
9
|
+
const dataRoot = path.join(homeDir, ".vesper");
|
|
10
|
+
const scriptPath0 = path.resolve(dataRoot, "python", "fusion_engine.py");
|
|
11
|
+
const scriptPath1 = path.resolve(buildDir, "python", "fusion_engine.py");
|
|
12
|
+
const scriptPath2 = path.resolve(buildDir, "..", "src", "python", "fusion_engine.py");
|
|
13
|
+
const scriptPath3 = path.resolve(buildDir, "..", "python", "fusion_engine.py");
|
|
14
|
+
if (fs.existsSync(scriptPath0)) {
|
|
15
|
+
this.scriptPath = scriptPath0;
|
|
16
|
+
}
|
|
17
|
+
else if (fs.existsSync(scriptPath1)) {
|
|
18
|
+
this.scriptPath = scriptPath1;
|
|
19
|
+
}
|
|
20
|
+
else if (fs.existsSync(scriptPath2)) {
|
|
21
|
+
this.scriptPath = scriptPath2;
|
|
22
|
+
}
|
|
23
|
+
else if (fs.existsSync(scriptPath3)) {
|
|
24
|
+
this.scriptPath = scriptPath3;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
this.scriptPath = scriptPath0;
|
|
28
|
+
}
|
|
29
|
+
if (process.platform === "win32") {
|
|
30
|
+
this.pythonPath = "py";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async fuse(sourcePaths, outputPath, options = {}) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
if (!Array.isArray(sourcePaths) || sourcePaths.length < 2) {
|
|
36
|
+
reject(new Error("At least 2 source paths are required for fusion"));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const args = [
|
|
40
|
+
this.scriptPath,
|
|
41
|
+
JSON.stringify(sourcePaths),
|
|
42
|
+
outputPath,
|
|
43
|
+
JSON.stringify(options),
|
|
44
|
+
];
|
|
45
|
+
const processRef = spawn(this.pythonPath, args);
|
|
46
|
+
let stdout = "";
|
|
47
|
+
let stderr = "";
|
|
48
|
+
processRef.stdout.on("data", (data) => (stdout += data.toString()));
|
|
49
|
+
processRef.stderr.on("data", (data) => (stderr += data.toString()));
|
|
50
|
+
processRef.on("close", (code) => {
|
|
51
|
+
if (code !== 0) {
|
|
52
|
+
reject(new Error(`Fusion failed: ${stderr || stdout}`));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const result = JSON.parse(stdout);
|
|
57
|
+
if (result.error) {
|
|
58
|
+
reject(new Error(result.error));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
resolve(result);
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
reject(new Error(`Failed to parse fusion output: ${stdout}`));
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|