knowledge-rag 3.6.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/LICENSE +21 -0
- package/README.md +61 -0
- package/bin/cli.js +254 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ailton Rocha (Lyon)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/knowledge-rag)
|
|
2
|
+
|
|
3
|
+
# Knowledge RAG
|
|
4
|
+
|
|
5
|
+
Local RAG system for Claude Code. Hybrid BM25 + semantic search with cross-encoder reranking. 12 MCP tools. Zero external servers. Everything runs on your machine.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx knowledge-rag
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The wrapper handles Python venv creation, package installation, and server startup automatically.
|
|
14
|
+
|
|
15
|
+
## Claude Code Configuration
|
|
16
|
+
|
|
17
|
+
Add to your MCP settings (`~/.claude.json` or project `.mcp.json`):
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"knowledge-rag": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": ["-y", "knowledge-rag"]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## CLI Flags
|
|
31
|
+
|
|
32
|
+
| Flag | Description |
|
|
33
|
+
|------|-------------|
|
|
34
|
+
| `--version` | Print version and exit |
|
|
35
|
+
| `--install-only` | Install the Python package into the venv without starting the server |
|
|
36
|
+
|
|
37
|
+
## Requirements
|
|
38
|
+
|
|
39
|
+
- **Node.js** >= 16
|
|
40
|
+
- **Python** >= 3.11
|
|
41
|
+
|
|
42
|
+
The wrapper auto-detects your Python installation across platforms (Windows: `python`, `py -3`; Linux/macOS: `python3`, `python`).
|
|
43
|
+
|
|
44
|
+
## How It Works
|
|
45
|
+
|
|
46
|
+
1. Finds a compatible Python 3.11+ interpreter on your system
|
|
47
|
+
2. Creates a persistent virtual environment at `~/.knowledge-rag/venv`
|
|
48
|
+
3. Installs the `knowledge-rag` PyPI package (skips if already up to date)
|
|
49
|
+
4. Starts the MCP server over stdio
|
|
50
|
+
|
|
51
|
+
The venv and installed package persist between runs. Reinstallation only happens when the NPM package version changes.
|
|
52
|
+
|
|
53
|
+
## Full Documentation
|
|
54
|
+
|
|
55
|
+
See the main repository for complete docs, configuration options, and tool reference:
|
|
56
|
+
|
|
57
|
+
**https://github.com/lyonzin/knowledge-rag**
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { execFileSync, spawn } = require("child_process");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const os = require("os");
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Constants
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
const PACKAGE_VERSION = require("../package.json").version;
|
|
15
|
+
const BASE_DIR = path.join(os.homedir(), ".knowledge-rag");
|
|
16
|
+
const VENV_DIR = path.join(BASE_DIR, "venv");
|
|
17
|
+
const VERSION_FILE = path.join(BASE_DIR, ".npm-version");
|
|
18
|
+
const PYPI_PACKAGE = "knowledge-rag";
|
|
19
|
+
const MIN_PYTHON_MAJOR = 3;
|
|
20
|
+
const MIN_PYTHON_MINOR = 11;
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Helpers — all user-facing output goes to STDERR (MCP uses stdout)
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
function log(msg) {
|
|
27
|
+
process.stderr.write(`[knowledge-rag] ${msg}\n`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function fatal(msg) {
|
|
31
|
+
process.stderr.write(`[knowledge-rag] ERROR: ${msg}\n`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Python discovery (cross-platform)
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
function tryPython(cmd, args) {
|
|
40
|
+
try {
|
|
41
|
+
const out = execFileSync(cmd, args, {
|
|
42
|
+
encoding: "utf-8",
|
|
43
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
44
|
+
timeout: 10000,
|
|
45
|
+
}).trim();
|
|
46
|
+
return out;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function parsePythonVersion(versionStr) {
|
|
53
|
+
const match = versionStr && versionStr.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
54
|
+
if (!match) return null;
|
|
55
|
+
return {
|
|
56
|
+
major: parseInt(match[1], 10),
|
|
57
|
+
minor: parseInt(match[2], 10),
|
|
58
|
+
patch: parseInt(match[3], 10),
|
|
59
|
+
raw: match[0],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function findPython() {
|
|
64
|
+
const isWindows = process.platform === "win32";
|
|
65
|
+
|
|
66
|
+
const candidates = isWindows
|
|
67
|
+
? [
|
|
68
|
+
{ cmd: "python", args: ["--version"] },
|
|
69
|
+
{ cmd: "py", args: ["-3", "--version"] },
|
|
70
|
+
]
|
|
71
|
+
: [
|
|
72
|
+
{ cmd: "python3", args: ["--version"] },
|
|
73
|
+
{ cmd: "python", args: ["--version"] },
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
for (const { cmd, args } of candidates) {
|
|
77
|
+
const raw = tryPython(cmd, args);
|
|
78
|
+
const ver = parsePythonVersion(raw);
|
|
79
|
+
if (!ver) continue;
|
|
80
|
+
if (ver.major < MIN_PYTHON_MAJOR) continue;
|
|
81
|
+
if (ver.major === MIN_PYTHON_MAJOR && ver.minor < MIN_PYTHON_MINOR) continue;
|
|
82
|
+
|
|
83
|
+
const execCmd = cmd === "py" ? "py" : cmd;
|
|
84
|
+
const execArgs = cmd === "py" ? ["-3"] : [];
|
|
85
|
+
return { cmd: execCmd, args: execArgs, version: ver };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Venv management
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
function getVenvPython() {
|
|
96
|
+
if (process.platform === "win32") {
|
|
97
|
+
return path.join(VENV_DIR, "Scripts", "python.exe");
|
|
98
|
+
}
|
|
99
|
+
return path.join(VENV_DIR, "bin", "python");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function venvExists() {
|
|
103
|
+
return fs.existsSync(getVenvPython());
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function createVenv(python) {
|
|
107
|
+
log(`Creating virtual environment at ${VENV_DIR}`);
|
|
108
|
+
fs.mkdirSync(BASE_DIR, { recursive: true });
|
|
109
|
+
|
|
110
|
+
const args = [...python.args, "-m", "venv", VENV_DIR];
|
|
111
|
+
try {
|
|
112
|
+
execFileSync(python.cmd, args, {
|
|
113
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
114
|
+
timeout: 120000,
|
|
115
|
+
});
|
|
116
|
+
} catch (err) {
|
|
117
|
+
fatal(
|
|
118
|
+
`Failed to create virtual environment.\n` +
|
|
119
|
+
` Command: ${python.cmd} ${args.join(" ")}\n` +
|
|
120
|
+
` ${err.stderr ? err.stderr.toString().trim() : err.message}`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function installedVersionMatches() {
|
|
126
|
+
try {
|
|
127
|
+
const stored = fs.readFileSync(VERSION_FILE, "utf-8").trim();
|
|
128
|
+
return stored === PACKAGE_VERSION;
|
|
129
|
+
} catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function writeVersionFile() {
|
|
135
|
+
fs.writeFileSync(VERSION_FILE, PACKAGE_VERSION, "utf-8");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function installPackage() {
|
|
139
|
+
const venvPython = getVenvPython();
|
|
140
|
+
log(`Installing ${PYPI_PACKAGE}==${PACKAGE_VERSION} from PyPI`);
|
|
141
|
+
|
|
142
|
+
const args = [
|
|
143
|
+
"-m",
|
|
144
|
+
"pip",
|
|
145
|
+
"install",
|
|
146
|
+
"--upgrade",
|
|
147
|
+
"--quiet",
|
|
148
|
+
`${PYPI_PACKAGE}==${PACKAGE_VERSION}`,
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
execFileSync(venvPython, args, {
|
|
153
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
154
|
+
timeout: 300000,
|
|
155
|
+
});
|
|
156
|
+
} catch (err) {
|
|
157
|
+
fatal(
|
|
158
|
+
`Failed to install ${PYPI_PACKAGE}.\n` +
|
|
159
|
+
` ${err.stderr ? err.stderr.toString().trim() : err.message}`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
writeVersionFile();
|
|
164
|
+
log("Installation complete");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// Ensure environment is ready
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
function ensureInstalled(python) {
|
|
172
|
+
if (!venvExists()) {
|
|
173
|
+
createVenv(python);
|
|
174
|
+
installPackage();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!installedVersionMatches()) {
|
|
179
|
+
log("Version changed, updating package");
|
|
180
|
+
installPackage();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// Run the MCP server
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
function runServer() {
|
|
190
|
+
const venvPython = getVenvPython();
|
|
191
|
+
|
|
192
|
+
const child = spawn(venvPython, ["-m", "mcp_server.server"], {
|
|
193
|
+
stdio: "inherit",
|
|
194
|
+
windowsHide: true,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const forwardSignal = (sig) => {
|
|
198
|
+
if (child.pid) {
|
|
199
|
+
try {
|
|
200
|
+
child.kill(sig);
|
|
201
|
+
} catch {
|
|
202
|
+
// child may have already exited
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
process.on("SIGINT", () => forwardSignal("SIGINT"));
|
|
208
|
+
process.on("SIGTERM", () => forwardSignal("SIGTERM"));
|
|
209
|
+
|
|
210
|
+
child.on("error", (err) => {
|
|
211
|
+
fatal(`Failed to start MCP server: ${err.message}`);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
child.on("exit", (code, signal) => {
|
|
215
|
+
if (signal) {
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
process.exit(code || 0);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
// CLI entry point
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
function main() {
|
|
227
|
+
const args = process.argv.slice(2);
|
|
228
|
+
|
|
229
|
+
if (args.includes("--version")) {
|
|
230
|
+
process.stderr.write(`knowledge-rag ${PACKAGE_VERSION}\n`);
|
|
231
|
+
process.exit(0);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const python = findPython();
|
|
235
|
+
if (!python) {
|
|
236
|
+
fatal(
|
|
237
|
+
`Python ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+ is required but was not found.\n` +
|
|
238
|
+
` Searched for: ${process.platform === "win32" ? "python, py -3" : "python3, python"}\n` +
|
|
239
|
+
` Install Python from https://www.python.org/downloads/`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
log(`Found Python ${python.version.raw}`);
|
|
244
|
+
ensureInstalled(python);
|
|
245
|
+
|
|
246
|
+
if (args.includes("--install-only")) {
|
|
247
|
+
log("Install complete (--install-only). Exiting.");
|
|
248
|
+
process.exit(0);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
runServer();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "knowledge-rag",
|
|
3
|
+
"version": "3.6.0",
|
|
4
|
+
"description": "Local RAG System for Claude Code — Hybrid search + Cross-encoder Reranking + 12 MCP Tools. Zero external servers.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"knowledge-rag": "./bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"rag",
|
|
10
|
+
"mcp",
|
|
11
|
+
"claude-code",
|
|
12
|
+
"semantic-search",
|
|
13
|
+
"embeddings",
|
|
14
|
+
"knowledge-base",
|
|
15
|
+
"local-ai",
|
|
16
|
+
"retrieval-augmented-generation",
|
|
17
|
+
"hybrid-search",
|
|
18
|
+
"claude"
|
|
19
|
+
],
|
|
20
|
+
"author": "Lyon. <lyonzin@users.noreply.github.com>",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"homepage": "https://github.com/lyonzin/knowledge-rag",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/lyonzin/knowledge-rag.git",
|
|
26
|
+
"directory": "npm"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/lyonzin/knowledge-rag/issues"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=16"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"bin/",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE"
|
|
38
|
+
]
|
|
39
|
+
}
|