n2-qln 3.4.1 → 4.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.
- package/README.ko.md +251 -262
- package/README.md +245 -276
- package/dist/index.d.ts +3 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +9 -0
- package/{lib → dist/lib}/config.js +23 -27
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/embedding.d.ts +27 -0
- package/{lib → dist/lib}/embedding.js +39 -47
- package/dist/lib/embedding.js.map +1 -0
- package/dist/lib/executor.d.ts +57 -0
- package/dist/lib/executor.js +175 -0
- package/dist/lib/executor.js.map +1 -0
- package/dist/lib/mcp-discovery.d.ts +83 -0
- package/dist/lib/mcp-discovery.js +203 -0
- package/dist/lib/mcp-discovery.js.map +1 -0
- package/dist/lib/provider-loader.d.ts +13 -0
- package/dist/lib/provider-loader.js +146 -0
- package/dist/lib/provider-loader.js.map +1 -0
- package/dist/lib/registry.d.ts +38 -0
- package/{lib → dist/lib}/registry.js +82 -92
- package/dist/lib/registry.js.map +1 -0
- package/dist/lib/router.d.ts +63 -0
- package/{lib → dist/lib}/router.js +75 -117
- package/dist/lib/router.js.map +1 -0
- package/dist/lib/schema.d.ts +20 -0
- package/{lib → dist/lib}/schema.js +38 -30
- package/dist/lib/schema.js.map +1 -0
- package/dist/lib/store.d.ts +37 -0
- package/dist/lib/store.js +207 -0
- package/dist/lib/store.js.map +1 -0
- package/dist/lib/validator.d.ts +37 -0
- package/dist/lib/validator.js +114 -0
- package/dist/lib/validator.js.map +1 -0
- package/dist/lib/vector-index.d.ts +37 -0
- package/{lib → dist/lib}/vector-index.js +19 -36
- package/dist/lib/vector-index.js.map +1 -0
- package/dist/tools/qln-call.d.ts +41 -0
- package/dist/tools/qln-call.js +353 -0
- package/dist/tools/qln-call.js.map +1 -0
- package/dist/tools/qln-helpers.d.ts +55 -0
- package/dist/tools/qln-helpers.js +88 -0
- package/dist/tools/qln-helpers.js.map +1 -0
- package/dist/types.d.ts +243 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/index.js +3 -79
- package/package.json +12 -5
- package/.github/FUNDING.yml +0 -3
- package/docs/README.md +0 -2
- package/docs/architecture.png +0 -0
- package/lib/executor.js +0 -104
- package/lib/provider-loader.js +0 -126
- package/lib/store.js +0 -217
- package/lib/validator.js +0 -171
- package/tools/qln-call.js +0 -257
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/n2-qln) [](LICENSE) [](https://nodejs.org) [](https://www.npmjs.com/package/n2-qln)
|
|
6
6
|
|
|
7
|
-
**QLN** = **Q**uery **L**ayer **N**etwork — a semantic
|
|
7
|
+
**QLN** = **Q**uery **L**ayer **N**etwork — a semantic tool router that sits between the AI and your tools.
|
|
8
8
|
|
|
9
9
|
> **Route 1,000+ tools through 1 MCP tool.** The AI sees only the router — not all 1,000 tools.
|
|
10
10
|
|
|
@@ -12,60 +12,97 @@
|
|
|
12
12
|
|
|
13
13
|
## Table of Contents
|
|
14
14
|
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [Setup](#setup)
|
|
15
|
+
- [Why QLN](#why-qln)
|
|
16
|
+
- [What's New in v4.1](#whats-new-in-v41)
|
|
17
|
+
- [Quick Start](#quick-start)
|
|
19
18
|
- [How It Works](#how-it-works)
|
|
20
19
|
- [API Reference](#api-reference)
|
|
20
|
+
- [MCP Auto-Discovery](#mcp-auto-discovery)
|
|
21
|
+
- [Provider Manifests](#provider-manifests)
|
|
21
22
|
- [Configuration](#configuration)
|
|
22
|
-
- [Semantic Search Setup](#semantic-search-setup-optional)
|
|
23
23
|
- [Project Structure](#project-structure)
|
|
24
|
-
- [Built & Battle-Tested](#built--battle-tested)
|
|
25
24
|
- [FAQ](#faq)
|
|
26
25
|
- [Contributing](#contributing)
|
|
27
26
|
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
🔍 **One tool to rule them all** — Your AI sees `n2_qln_call` (~200 tokens), not 1,000 individual tools. 99.6% context reduction.
|
|
27
|
+
## Why QLN
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
Every MCP tool eats context tokens. 10 tools? Fine. 100? Slow. **1,000? Impossible** — context is full before the conversation starts.
|
|
33
30
|
|
|
34
|
-
|
|
31
|
+
QLN solves this:
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
1. All tools are indexed in QLN's SQLite engine
|
|
34
|
+
2. The AI sees **one tool**: `n2_qln_call` (~200 tokens)
|
|
35
|
+
3. AI searches → finds the best match → executes with automatic fallback
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
**Result: ~200 tokens instead of ~50,000. 99.6% reduction.**
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
## Features
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
| Feature | Description |
|
|
42
|
+
|---------|-------------|
|
|
43
|
+
| **1 tool = 1,000 tools** | AI sees `n2_qln_call` (~200 tokens), QLN routes to the right one |
|
|
44
|
+
| **Sub-5ms search** | 3-stage engine: trigger match → BM25 keyword → semantic vector |
|
|
45
|
+
| **Auto mode** | One-shot search + execute with confidence gating and fallback chain |
|
|
46
|
+
| **Circuit Breaker** | Auto-disable failing tools, self-recover after timeout |
|
|
47
|
+
| **MCP Auto-Discovery** | Scan external MCP servers and index their tools automatically |
|
|
48
|
+
| **Boost Keywords** | Curated terms with 2× BM25 weight for precision search |
|
|
49
|
+
| **Self-learning ranking** | Usage count + success rate feed back into scores |
|
|
50
|
+
| **Source weighting** | Prioritize tools by origin (mcp > plugin > local) |
|
|
51
|
+
| **Hot reload** | Edit `providers/` manifests at runtime — auto re-indexed |
|
|
52
|
+
| **Bulk inject** | Register hundreds of tools in one call |
|
|
53
|
+
| **Enforced validation** | `verb_target` naming, min description length, category constraints |
|
|
54
|
+
| **Semantic search** | Optional Ollama embeddings for natural language matching |
|
|
55
|
+
| **Zero native deps** | SQLite via [sql.js](https://github.com/sql-js/sql.js) WASM — `npm install` and done |
|
|
56
|
+
| **Dual execution** | Local function handlers or HTTP proxy — mix and match |
|
|
57
|
+
| **TypeScript strict** | Full strict-mode codebase since v4.0 |
|
|
58
|
+
|
|
59
|
+
## What's New in v4.1
|
|
60
|
+
|
|
61
|
+
### 🔍 MCP Auto-Discovery
|
|
62
|
+
|
|
63
|
+
Scan connected MCP servers and auto-index their tools — QLN becomes a **universal MCP hub**.
|
|
43
64
|
|
|
44
|
-
|
|
65
|
+
```javascript
|
|
66
|
+
n2_qln_call({
|
|
67
|
+
action: "discover",
|
|
68
|
+
servers: [
|
|
69
|
+
{ name: "my-server", command: "node", args: ["server.js"] }
|
|
70
|
+
]
|
|
71
|
+
})
|
|
72
|
+
// → Discovered 47 tools from my-server (320ms)
|
|
73
|
+
```
|
|
45
74
|
|
|
46
|
-
|
|
75
|
+
### ⚡ Circuit Breaker
|
|
47
76
|
|
|
48
|
-
|
|
77
|
+
Tools that fail 3 times in a row are automatically disabled. After 60 seconds, QLN attempts recovery. No cascading failures, no wasted requests.
|
|
49
78
|
|
|
50
|
-
|
|
79
|
+
```
|
|
80
|
+
closed → 3 failures → open (fast-fail) → 60s → half-open (retry) → success → closed
|
|
81
|
+
```
|
|
51
82
|
|
|
52
|
-
|
|
83
|
+
### 🔄 Fallback Chain
|
|
53
84
|
|
|
54
|
-
|
|
85
|
+
`auto` mode now tries up to 3 ranked candidates. If the top match fails, QLN automatically falls through to the next best tool.
|
|
55
86
|
|
|
56
|
-
|
|
87
|
+
```
|
|
88
|
+
auto "send notification" → try push_notification ❌ → try send_email ✅
|
|
89
|
+
```
|
|
57
90
|
|
|
58
|
-
|
|
91
|
+
### 🎯 Boost Keywords
|
|
59
92
|
|
|
60
|
-
|
|
61
|
-
2. The AI sees only **one tool**: `n2_qln_call` (~200 tokens)
|
|
62
|
-
3. When the AI needs a tool, it **searches** → **finds the best match** → **executes**
|
|
93
|
+
Add curated search terms to tools via `boostKeywords`. These get 2× weight in BM25 ranking, improving discoverability without adding context overhead.
|
|
63
94
|
|
|
64
|
-
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"name": "send_email",
|
|
98
|
+
"description": "Send an email to a recipient",
|
|
99
|
+
"boostKeywords": "smtp outbound notification mail"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
65
102
|
|
|
66
103
|
---
|
|
67
104
|
|
|
68
|
-
##
|
|
105
|
+
## Quick Start
|
|
69
106
|
|
|
70
107
|
```bash
|
|
71
108
|
npm install n2-qln
|
|
@@ -73,20 +110,12 @@ npm install n2-qln
|
|
|
73
110
|
|
|
74
111
|
**Requirements:** Node.js ≥ 18
|
|
75
112
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## Setup
|
|
81
|
-
|
|
82
|
-
QLN is an MCP server. You connect it to any MCP-compatible AI client — Claude Desktop, Cursor, n2-soul, or any other host.
|
|
83
|
-
|
|
84
|
-
### Claude Desktop
|
|
113
|
+
### Connect to an MCP Client
|
|
85
114
|
|
|
86
|
-
|
|
115
|
+
<details>
|
|
116
|
+
<summary><strong>Claude Desktop</strong></summary>
|
|
87
117
|
|
|
88
|
-
|
|
89
|
-
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
118
|
+
Edit `claude_desktop_config.json`:
|
|
90
119
|
|
|
91
120
|
```json
|
|
92
121
|
{
|
|
@@ -98,12 +127,12 @@ Edit your Claude Desktop config file:
|
|
|
98
127
|
}
|
|
99
128
|
}
|
|
100
129
|
```
|
|
130
|
+
</details>
|
|
101
131
|
|
|
102
|
-
|
|
132
|
+
<details>
|
|
133
|
+
<summary><strong>Cursor</strong></summary>
|
|
103
134
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Open **Settings → MCP Servers → Add Server** and configure:
|
|
135
|
+
Open **Settings → MCP Servers → Add Server**:
|
|
107
136
|
|
|
108
137
|
```json
|
|
109
138
|
{
|
|
@@ -112,323 +141,266 @@ Open **Settings → MCP Servers → Add Server** and configure:
|
|
|
112
141
|
"args": ["-y", "n2-qln"]
|
|
113
142
|
}
|
|
114
143
|
```
|
|
144
|
+
</details>
|
|
115
145
|
|
|
116
|
-
|
|
146
|
+
<details>
|
|
147
|
+
<summary><strong>Any MCP Client</strong></summary>
|
|
117
148
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
```javascript
|
|
121
|
-
module.exports = {
|
|
122
|
-
mcpServers: {
|
|
123
|
-
'n2-qln': {
|
|
124
|
-
command: 'node',
|
|
125
|
-
args: ['<path-to-qln>/index.js'],
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Or if published to npm:
|
|
132
|
-
|
|
133
|
-
```javascript
|
|
134
|
-
module.exports = {
|
|
135
|
-
mcpServers: {
|
|
136
|
-
'n2-qln': {
|
|
137
|
-
command: 'npx',
|
|
138
|
-
args: ['-y', 'n2-qln'],
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### Any MCP Client
|
|
145
|
-
|
|
146
|
-
QLN uses **stdio transport** — the standard MCP communication method. Any MCP-compatible client can connect using:
|
|
149
|
+
QLN uses **stdio transport** — the MCP standard.
|
|
147
150
|
|
|
148
151
|
```
|
|
149
152
|
command: npx
|
|
150
153
|
args: ["-y", "n2-qln"]
|
|
151
154
|
```
|
|
152
155
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
```
|
|
156
|
-
command: node
|
|
157
|
-
args: ["/absolute/path/to/n2-qln/index.js"]
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
> **💡 Tip:** The easiest way to set this up? **Just ask your AI agent.** Tell it *"Add n2-qln to my MCP config"* — it already knows how to configure itself.
|
|
156
|
+
> **Tip:** Just ask your AI agent — *"Add n2-qln to my MCP config."*
|
|
157
|
+
</details>
|
|
161
158
|
|
|
162
159
|
---
|
|
163
160
|
|
|
164
161
|
## How It Works
|
|
165
162
|
|
|
166
|
-
### Step-by-Step Example
|
|
167
|
-
|
|
168
163
|
```
|
|
169
164
|
User: "Take a screenshot of this page"
|
|
170
165
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
Step 2 → AI calls: n2_qln_call(action: "exec", tool: "take_screenshot", args: {fullPage: true})
|
|
176
|
-
QLN routes to the actual tool and executes it
|
|
177
|
-
Response: ✅ screenshot saved
|
|
166
|
+
AI → n2_qln_call(action: "auto", query: "screenshot page")
|
|
167
|
+
QLN → 3-stage search (< 5ms) → take_screenshot (score: 8.0)
|
|
168
|
+
→ execute → fallback if needed → result
|
|
178
169
|
```
|
|
179
170
|
|
|
180
|
-
The AI only used `n2_qln_call`. It never saw the other 999 tools.
|
|
181
|
-
|
|
182
171
|
### 3-Stage Search Engine
|
|
183
172
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
| Stage | Method | Speed | How it works |
|
|
173
|
+
| Stage | Method | Speed | Details |
|
|
187
174
|
|:---:|--------|:---:|------|
|
|
188
|
-
| **1** | Trigger Match |
|
|
189
|
-
| **2** | BM25 Keyword |
|
|
190
|
-
| **3** | Semantic Search |
|
|
175
|
+
| **1** | Trigger Match | <1ms | Exact keyword match on tool names and triggers |
|
|
176
|
+
| **2** | BM25 Keyword | 1-3ms | [Okapi BM25](https://en.wikipedia.org/wiki/Okapi_BM25) — IDF weighting, length normalization, `boostKeywords` 2× boost |
|
|
177
|
+
| **3** | Semantic Search | 5-15ms | Vector similarity via [Ollama](https://ollama.ai) embeddings *(optional)* |
|
|
191
178
|
|
|
192
|
-
Results
|
|
179
|
+
Results are merged and ranked:
|
|
193
180
|
|
|
194
181
|
```
|
|
195
|
-
final_score =
|
|
196
|
-
+
|
|
197
|
-
+ semantic_score × 2.0
|
|
198
|
-
+ log2(usage_count + 1) × 0.5
|
|
199
|
-
+ success_rate × 1.0
|
|
182
|
+
final_score = trigger × 3.0 + bm25 × 1.0 + semantic × 2.0
|
|
183
|
+
+ log₂(usage + 1) × 0.5 + success_rate × 1.0
|
|
200
184
|
```
|
|
201
185
|
|
|
202
|
-
Tools that are used more often and succeed more reliably are ranked higher over time.
|
|
203
|
-
|
|
204
186
|
---
|
|
205
187
|
|
|
206
188
|
## API Reference
|
|
207
189
|
|
|
208
|
-
QLN exposes **one MCP tool** — `n2_qln_call` — with
|
|
190
|
+
QLN exposes **one MCP tool** — `n2_qln_call` — with 9 actions.
|
|
191
|
+
|
|
192
|
+
### auto — Search + Execute (one-shot)
|
|
209
193
|
|
|
210
|
-
|
|
194
|
+
The recommended action. Searches, picks the best match, executes with fallback chain.
|
|
211
195
|
|
|
212
196
|
```javascript
|
|
213
197
|
n2_qln_call({
|
|
214
|
-
action: "
|
|
215
|
-
query: "take a screenshot",
|
|
216
|
-
|
|
217
|
-
topK: 5 // max results, default: 5 (optional)
|
|
198
|
+
action: "auto",
|
|
199
|
+
query: "take a screenshot", // natural language (required)
|
|
200
|
+
args: { fullPage: true } // passed to the matched tool (optional)
|
|
218
201
|
})
|
|
202
|
+
// → [auto] "take a screenshot" → take_screenshot (score: 8.0, 2ms search + 150ms exec)
|
|
219
203
|
```
|
|
220
204
|
|
|
221
|
-
**
|
|
222
|
-
|
|
223
|
-
|
|
205
|
+
**Confidence gate:** If the top score is below 2.0, QLN returns search results instead of auto-executing — preventing wrong tool execution.
|
|
206
|
+
|
|
207
|
+
**Fallback chain:** If the top match fails, QLN automatically tries the next 2 ranked candidates before giving up.
|
|
224
208
|
|
|
225
|
-
|
|
226
|
-
Take a full-page or viewport screenshot
|
|
227
|
-
Triggers: take_screenshot, screenshot, capture
|
|
209
|
+
### search — Find tools
|
|
228
210
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
211
|
+
```javascript
|
|
212
|
+
n2_qln_call({
|
|
213
|
+
action: "search",
|
|
214
|
+
query: "send email notification",
|
|
215
|
+
topK: 5 // max results (default: 5, max: 20)
|
|
216
|
+
})
|
|
232
217
|
```
|
|
233
218
|
|
|
234
|
-
### exec — Execute a tool
|
|
219
|
+
### exec — Execute a specific tool
|
|
235
220
|
|
|
236
221
|
```javascript
|
|
237
222
|
n2_qln_call({
|
|
238
223
|
action: "exec",
|
|
239
|
-
tool: "take_screenshot",
|
|
240
|
-
args: {
|
|
241
|
-
fullPage: true,
|
|
242
|
-
format: "png"
|
|
243
|
-
}
|
|
224
|
+
tool: "take_screenshot",
|
|
225
|
+
args: { fullPage: true, format: "png" }
|
|
244
226
|
})
|
|
245
227
|
```
|
|
246
228
|
|
|
247
|
-
### create — Register a
|
|
229
|
+
### create — Register a tool
|
|
248
230
|
|
|
249
231
|
```javascript
|
|
250
232
|
n2_qln_call({
|
|
251
233
|
action: "create",
|
|
252
|
-
name: "read_pdf",
|
|
253
|
-
description: "Read and extract text from PDF files",
|
|
254
|
-
category: "data",
|
|
255
|
-
|
|
256
|
-
tags: ["pdf", "read", "extract"
|
|
257
|
-
|
|
258
|
-
"read this PDF file",
|
|
259
|
-
"extract text from PDF",
|
|
260
|
-
"open the PDF"
|
|
261
|
-
],
|
|
262
|
-
endpoint: "http://127.0.0.1:3100", // optional, for HTTP-based tools
|
|
263
|
-
toolSchema: { filePath: { type: "string" } } // optional, input schema
|
|
234
|
+
name: "read_pdf", // verb_target format (required)
|
|
235
|
+
description: "Read and extract text from PDF files", // min 10 chars (required)
|
|
236
|
+
category: "data", // web|data|file|dev|ai|capture|misc
|
|
237
|
+
boostKeywords: "pdf extract parse document text", // BM25 boost terms
|
|
238
|
+
tags: ["pdf", "read", "extract"],
|
|
239
|
+
endpoint: "http://127.0.0.1:3100" // for HTTP-based tools
|
|
264
240
|
})
|
|
265
241
|
```
|
|
266
242
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
| Rule | Requirement | Example |
|
|
270
|
-
|------|------------|---------|
|
|
271
|
-
| **Name** | `verb_target` format (lowercase + underscore) | `read_pdf`, `take_screenshot` |
|
|
272
|
-
| **Description** | Minimum 10 characters | `"Read and extract text from PDF files"` |
|
|
273
|
-
| **Category** | Must be one of the valid categories | `"data"` |
|
|
274
|
-
| **Unique** | No duplicate names allowed | — |
|
|
275
|
-
|
|
276
|
-
```
|
|
277
|
-
❌ pdfReader → Rejected: not verb_target format
|
|
278
|
-
❌ "PDF tool" → Rejected: description under 10 characters
|
|
279
|
-
❌ read_pdf (exists)→ Rejected: duplicate name, use action: "update"
|
|
280
|
-
✅ read_pdf → Accepted
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
**Valid categories:** `web` · `data` · `file` · `dev` · `ai` · `capture` · `misc`
|
|
284
|
-
|
|
285
|
-
### update — Modify an existing tool
|
|
243
|
+
### inject — Bulk register
|
|
286
244
|
|
|
287
245
|
```javascript
|
|
288
246
|
n2_qln_call({
|
|
289
|
-
action: "
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
247
|
+
action: "inject",
|
|
248
|
+
source: "my-plugin",
|
|
249
|
+
tools: [
|
|
250
|
+
{ name: "tool_a", description: "Does A", category: "misc" },
|
|
251
|
+
{ name: "tool_b", description: "Does B", category: "dev" }
|
|
252
|
+
]
|
|
294
253
|
})
|
|
295
254
|
```
|
|
296
255
|
|
|
297
|
-
|
|
256
|
+
### discover — Scan MCP servers
|
|
298
257
|
|
|
299
|
-
|
|
258
|
+
See [MCP Auto-Discovery](#mcp-auto-discovery).
|
|
259
|
+
|
|
260
|
+
### update / delete / stats
|
|
300
261
|
|
|
301
262
|
```javascript
|
|
302
|
-
//
|
|
303
|
-
n2_qln_call({
|
|
304
|
-
action: "delete",
|
|
305
|
-
tool: "read_pdf"
|
|
306
|
-
})
|
|
263
|
+
// Update a field
|
|
264
|
+
n2_qln_call({ action: "update", tool: "read_pdf", description: "Enhanced PDF reader" })
|
|
307
265
|
|
|
308
|
-
// Delete
|
|
309
|
-
n2_qln_call({
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
266
|
+
// Delete by name or provider
|
|
267
|
+
n2_qln_call({ action: "delete", tool: "read_pdf" })
|
|
268
|
+
n2_qln_call({ action: "delete", provider: "pdf-tools" })
|
|
269
|
+
|
|
270
|
+
// System stats (includes Circuit Breaker status)
|
|
271
|
+
n2_qln_call({ action: "stats" })
|
|
314
272
|
```
|
|
315
273
|
|
|
316
274
|
---
|
|
317
275
|
|
|
318
|
-
##
|
|
276
|
+
## MCP Auto-Discovery
|
|
319
277
|
|
|
320
|
-
|
|
278
|
+
The killer feature of v4.1. Connect any MCP server and QLN auto-indexes all its tools.
|
|
321
279
|
|
|
322
280
|
```javascript
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
},
|
|
331
|
-
};
|
|
281
|
+
n2_qln_call({
|
|
282
|
+
action: "discover",
|
|
283
|
+
servers: [
|
|
284
|
+
{ name: "n2-soul", command: "node", args: ["path/to/soul/index.js"] },
|
|
285
|
+
{ name: "github", command: "npx", args: ["-y", "@modelcontextprotocol/server-github"] }
|
|
286
|
+
]
|
|
287
|
+
})
|
|
332
288
|
```
|
|
333
289
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
290
|
+
**What happens:**
|
|
291
|
+
1. QLN connects to each server via stdio
|
|
292
|
+
2. Lists all tools via `tools/list`
|
|
293
|
+
3. Registers them as `mcp__servername__toolname` in the QLN index
|
|
294
|
+
4. Auto-generates `boostKeywords` from tool names and descriptions
|
|
295
|
+
5. Keeps connections alive for live execution
|
|
337
296
|
|
|
338
|
-
|
|
297
|
+
**Re-discovery is idempotent** — run it again and old entries are purged before re-registering.
|
|
339
298
|
|
|
340
|
-
|
|
299
|
+
---
|
|
341
300
|
|
|
342
|
-
|
|
301
|
+
## Provider Manifests
|
|
343
302
|
|
|
344
|
-
|
|
303
|
+
Drop a JSON file in `providers/` and tools are auto-indexed at boot. No code changes, no manual calls.
|
|
345
304
|
|
|
346
|
-
|
|
305
|
+
```json
|
|
306
|
+
{
|
|
307
|
+
"provider": "my-tools",
|
|
308
|
+
"version": "1.0.0",
|
|
309
|
+
"tools": [
|
|
310
|
+
{
|
|
311
|
+
"name": "send_email",
|
|
312
|
+
"description": "Send an email to a recipient",
|
|
313
|
+
"category": "communication",
|
|
314
|
+
"triggers": ["email", "send", "mail"],
|
|
315
|
+
"boostKeywords": "smtp outbound notification"
|
|
316
|
+
}
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
```
|
|
347
320
|
|
|
348
|
-
|
|
321
|
+
Hot reload: edit a manifest while QLN is running — changes are picked up automatically.
|
|
349
322
|
|
|
350
|
-
|
|
351
|
-
ollama pull nomic-embed-text
|
|
352
|
-
```
|
|
323
|
+
---
|
|
353
324
|
|
|
354
|
-
|
|
325
|
+
## Configuration
|
|
355
326
|
|
|
356
|
-
|
|
327
|
+
Zero config required. For customization, create `config.local.js`:
|
|
357
328
|
|
|
358
329
|
```javascript
|
|
359
330
|
module.exports = {
|
|
331
|
+
dataDir: './data',
|
|
332
|
+
|
|
333
|
+
// Stage 3 semantic search (optional — Stage 1+2 work without this)
|
|
360
334
|
embedding: {
|
|
361
335
|
enabled: true,
|
|
362
336
|
provider: 'ollama',
|
|
363
|
-
model: 'nomic-embed-text',
|
|
337
|
+
model: 'nomic-embed-text', // or 'bge-m3' for multilingual
|
|
364
338
|
baseUrl: 'http://127.0.0.1:11434',
|
|
365
339
|
},
|
|
366
|
-
};
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Comparison
|
|
370
|
-
|
|
371
|
-
| Setup | Search Stages | Accuracy | Dependencies |
|
|
372
|
-
|:------|:---:|:---:|:---:|
|
|
373
|
-
| **Default** (no Ollama) | Stage 1 + 2 | ⭐⭐⭐⭐ Great | None |
|
|
374
|
-
| **With Ollama** | Stage 1 + 2 + 3 | ⭐⭐⭐⭐⭐ Perfect | Ollama running |
|
|
375
340
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
341
|
+
// Tool execution
|
|
342
|
+
executor: {
|
|
343
|
+
timeout: 20000, // execution timeout (ms)
|
|
344
|
+
circuitBreaker: {
|
|
345
|
+
failureThreshold: 3, // consecutive failures before tripping
|
|
346
|
+
recoveryTimeout: 60000, // ms before recovery attempt
|
|
347
|
+
},
|
|
348
|
+
},
|
|
379
349
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
350
|
+
// Source weight multipliers for search ranking (v4.0)
|
|
351
|
+
// Higher weight = higher priority in results
|
|
352
|
+
search: {
|
|
353
|
+
sourceWeights: {
|
|
354
|
+
mcp: 1.5, // MCP-discovered tools ranked highest
|
|
355
|
+
provider: 1.2, // Provider manifest tools
|
|
356
|
+
local: 1.0, // Manually created tools (default)
|
|
357
|
+
},
|
|
358
|
+
},
|
|
383
359
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
enabled: true,
|
|
389
|
-
model: 'bge-m3', // multilingual (100+ languages)
|
|
360
|
+
// Provider auto-indexing
|
|
361
|
+
providers: {
|
|
362
|
+
enabled: true, // auto-load providers/*.json at boot
|
|
363
|
+
dir: './providers', // manifest directory
|
|
390
364
|
},
|
|
391
365
|
};
|
|
392
366
|
```
|
|
393
367
|
|
|
394
|
-
|
|
368
|
+
> `config.local.js` is gitignored. Cloud sync: point `dataDir` to Google Drive / OneDrive / NAS.
|
|
395
369
|
|
|
396
|
-
###
|
|
370
|
+
### Semantic Search (Optional)
|
|
397
371
|
|
|
398
|
-
|
|
372
|
+
Without Ollama, Stage 1 + 2 already deliver great results.
|
|
399
373
|
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
};
|
|
374
|
+
```bash
|
|
375
|
+
ollama pull nomic-embed-text # English-optimized
|
|
376
|
+
# or
|
|
377
|
+
ollama pull bge-m3 # Multilingual (100+ languages)
|
|
405
378
|
```
|
|
406
379
|
|
|
407
|
-
Same approach as [n2-soul Cloud Storage](https://github.com/choihyunsus/soul#%EF%B8%8F-cloud-storage--store-your-ai-memory-anywhere). SQLite file lives in that folder — your sync service handles the rest.
|
|
408
|
-
|
|
409
380
|
---
|
|
410
381
|
|
|
411
382
|
## Project Structure
|
|
412
383
|
|
|
413
384
|
```
|
|
414
385
|
n2-qln/
|
|
415
|
-
├──
|
|
416
|
-
├──
|
|
417
|
-
│ ├──
|
|
418
|
-
│
|
|
419
|
-
│
|
|
420
|
-
│
|
|
421
|
-
│
|
|
422
|
-
│
|
|
423
|
-
│
|
|
424
|
-
│
|
|
425
|
-
│
|
|
426
|
-
│
|
|
427
|
-
├──
|
|
428
|
-
│
|
|
429
|
-
|
|
430
|
-
├──
|
|
431
|
-
|
|
386
|
+
├── src/
|
|
387
|
+
│ ├── index.ts # MCP server entry point
|
|
388
|
+
│ ├── types.ts # Shared type definitions
|
|
389
|
+
│ └── lib/
|
|
390
|
+
│ ├── config.ts # Config loader
|
|
391
|
+
│ ├── store.ts # SQLite engine (sql.js WASM)
|
|
392
|
+
│ ├── schema.ts # Tool normalization + boostKeywords builder
|
|
393
|
+
│ ├── validator.ts # Enforced validation (name, desc, category)
|
|
394
|
+
│ ├── registry.ts # Tool CRUD + usage tracking + circuit breaker stats
|
|
395
|
+
│ ├── router.ts # 3-stage parallel search (BM25)
|
|
396
|
+
│ ├── vector-index.ts # Float32 centroid hierarchy
|
|
397
|
+
│ ├── embedding.ts # Ollama embedding client
|
|
398
|
+
│ ├── executor.ts # HTTP/function executor + Circuit Breaker
|
|
399
|
+
│ ├── mcp-discovery.ts # MCP Auto-Discovery engine
|
|
400
|
+
│ └── provider-loader.ts
|
|
401
|
+
├── providers/ # Tool manifests (auto-indexed at boot)
|
|
402
|
+
├── config.local.js # Local overrides (gitignored)
|
|
403
|
+
└── data/ # SQLite database (gitignored)
|
|
432
404
|
```
|
|
433
405
|
|
|
434
406
|
## Tech Stack
|
|
@@ -436,46 +408,43 @@ n2-qln/
|
|
|
436
408
|
| Component | Technology | Why |
|
|
437
409
|
|-----------|-----------|-----|
|
|
438
410
|
| Runtime | Node.js ≥ 18 | MCP SDK compatibility |
|
|
439
|
-
| Database | SQLite via [sql.js](https://github.com/sql-js/sql.js) (WASM) | Zero native deps, cross-platform
|
|
440
|
-
| Embeddings | [Ollama](https://ollama.ai)
|
|
441
|
-
| Protocol | [MCP](https://modelcontextprotocol.io)
|
|
442
|
-
|
|
|
411
|
+
| Database | SQLite via [sql.js](https://github.com/sql-js/sql.js) (WASM) | Zero native deps, cross-platform |
|
|
412
|
+
| Embeddings | [Ollama](https://ollama.ai) | Local, fast, free, optional |
|
|
413
|
+
| Protocol | [MCP](https://modelcontextprotocol.io) | Standard AI tool protocol |
|
|
414
|
+
| Language | TypeScript (strict) | Type-safe, maintainable |
|
|
443
415
|
|
|
444
416
|
## Related Projects
|
|
445
417
|
|
|
446
418
|
| Project | Relationship |
|
|
447
419
|
|---------|-------------|
|
|
448
|
-
| [n2-soul](https://github.com/choihyunsus/soul) | AI agent orchestrator — QLN
|
|
420
|
+
| [n2-soul](https://github.com/choihyunsus/soul) | AI agent orchestrator — QLN is Soul's tool brain |
|
|
449
421
|
|
|
450
422
|
## Built & Battle-Tested
|
|
451
423
|
|
|
452
|
-
|
|
424
|
+
QLN has been **tested in production for 2+ months** as the core tool router for [n2-soul](https://github.com/choihyunsus/soul). Not a prototype — a daily driver.
|
|
453
425
|
|
|
454
|
-
Written by **Rose**
|
|
455
|
-
|
|
456
|
-
If you run into issues or have ideas, feel free to open an issue. We'd love to hear how you use it.
|
|
426
|
+
Written by **Rose** — N2's first AI agent.
|
|
457
427
|
|
|
458
428
|
## FAQ
|
|
459
429
|
|
|
460
|
-
**"Why
|
|
430
|
+
**"Why one tool instead of many?"**
|
|
461
431
|
|
|
462
|
-
|
|
432
|
+
Context tokens. Every tool definition costs 50-200 tokens. 100 tools = 10,000 tokens *gone* before the conversation starts. QLN gives you 1,000+ tools for ~200 tokens.
|
|
463
433
|
|
|
464
|
-
|
|
434
|
+
**"What if the search picks the wrong tool?"**
|
|
465
435
|
|
|
466
|
-
|
|
436
|
+
The fallback chain (v4.1) auto-retries with the next best match. Plus tools self-learn — frequently used + successful tools rank higher over time.
|
|
467
437
|
|
|
468
|
-
|
|
438
|
+
**"Do I need Ollama?"**
|
|
469
439
|
|
|
470
|
-
1.
|
|
471
|
-
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
472
|
-
3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
|
|
473
|
-
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
474
|
-
5. Open a Pull Request
|
|
440
|
+
No. Stage 1 (trigger) + Stage 2 (BM25) handle most cases. Ollama adds semantic understanding for edge cases — nice to have, not required.
|
|
475
441
|
|
|
476
|
-
##
|
|
442
|
+
## Contributing
|
|
477
443
|
|
|
478
|
-
|
|
444
|
+
1. Fork the repo
|
|
445
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
446
|
+
3. Commit (`git commit -m 'feat: add amazing feature'`)
|
|
447
|
+
4. Push and open a PR
|
|
479
448
|
|
|
480
449
|
## License
|
|
481
450
|
|
|
@@ -485,6 +454,6 @@ Apache-2.0
|
|
|
485
454
|
|
|
486
455
|
> *"1,000 tools in 200 tokens. That's not optimization — that's a paradigm shift."*
|
|
487
456
|
|
|
488
|
-
|
|
457
|
+
🔗 [nton2.com](https://nton2.com) · [npm](https://www.npmjs.com/package/n2-qln) · lagi0730@gmail.com
|
|
489
458
|
|
|
490
|
-
<sub
|
|
459
|
+
<sub>Built by Rose — N2's first AI agent. I search through QLN hundreds of times a day, and I wrote this README too.</sub>
|