n2-qln 3.4.1 โ 3.4.2
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 +470 -470
- package/README.md +490 -490
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,490 +1,490 @@
|
|
|
1
|
-
๐ฐ๐ท [ํ๊ตญ์ด](README.ko.md)
|
|
2
|
-
|
|
3
|
-
# n2-qln
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/n2-qln) [](LICENSE) [](https://nodejs.org) [](https://www.npmjs.com/package/n2-qln)
|
|
6
|
-
|
|
7
|
-
**QLN** = **Q**uery **L**ayer **N**etwork โ a semantic search layer that sits between the AI and your tools.
|
|
8
|
-
|
|
9
|
-
> **Route 1,000+ tools through 1 MCP tool.** The AI sees only the router โ not all 1,000 tools.
|
|
10
|
-
|
|
11
|
-

|
|
12
|
-
|
|
13
|
-
## Table of Contents
|
|
14
|
-
|
|
15
|
-
- [Features](#features)
|
|
16
|
-
- [The Problem](#the-problem)
|
|
17
|
-
- [Installation](#installation)
|
|
18
|
-
- [Setup](#setup)
|
|
19
|
-
- [How It Works](#how-it-works)
|
|
20
|
-
- [API Reference](#api-reference)
|
|
21
|
-
- [Configuration](#configuration)
|
|
22
|
-
- [Semantic Search Setup](#semantic-search-setup-optional)
|
|
23
|
-
- [Project Structure](#project-structure)
|
|
24
|
-
- [Built & Battle-Tested](#built--battle-tested)
|
|
25
|
-
- [FAQ](#faq)
|
|
26
|
-
- [Contributing](#contributing)
|
|
27
|
-
|
|
28
|
-
## Features
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
## The Problem
|
|
55
|
-
|
|
56
|
-
Every MCP tool you register eats AI context tokens. With 10 tools that's manageable. With 100, the AI slows down. **With 1,000, it's impossible** โ the context window is full before the conversation even starts.
|
|
57
|
-
|
|
58
|
-
QLN solves this by acting as a **semantic search router**:
|
|
59
|
-
|
|
60
|
-
1. Register all your tools in QLN's SQLite index
|
|
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**
|
|
63
|
-
|
|
64
|
-
**Result: ~200 tokens instead of ~50,000. 99.6% reduction.**
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Installation
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
npm install n2-qln
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
**Requirements:** Node.js โฅ 18
|
|
75
|
-
|
|
76
|
-
**Optional:** Install [Ollama](https://ollama.ai) for semantic vector search (Stage 3). See [Semantic Search Setup](#semantic-search-setup-optional).
|
|
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
|
|
85
|
-
|
|
86
|
-
Edit your Claude Desktop config file:
|
|
87
|
-
|
|
88
|
-
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
89
|
-
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
90
|
-
|
|
91
|
-
```json
|
|
92
|
-
{
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Restart Claude Desktop. The `n2_qln_call` tool will appear in your tool list.
|
|
103
|
-
|
|
104
|
-
### Cursor
|
|
105
|
-
|
|
106
|
-
Open **Settings โ MCP Servers โ Add Server** and configure:
|
|
107
|
-
|
|
108
|
-
```json
|
|
109
|
-
{
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### n2-soul
|
|
117
|
-
|
|
118
|
-
Add to your Soul `config.local.js`:
|
|
119
|
-
|
|
120
|
-
```javascript
|
|
121
|
-
module.exports = {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
};
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Or if published to npm:
|
|
132
|
-
|
|
133
|
-
```javascript
|
|
134
|
-
module.exports = {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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:
|
|
147
|
-
|
|
148
|
-
```
|
|
149
|
-
command: npx
|
|
150
|
-
args: ["-y", "n2-qln"]
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
Or if you cloned the repo:
|
|
154
|
-
|
|
155
|
-
```
|
|
156
|
-
command: node
|
|
157
|
-
args: ["/absolute/path/to/n2-qln/index.js"]
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
>
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## How It Works
|
|
165
|
-
|
|
166
|
-
### Step-by-Step Example
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
User: "Take a screenshot of this page"
|
|
170
|
-
|
|
171
|
-
Step 1 โ AI calls: n2_qln_call(action: "search", query: "screenshot page")
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
Step 2 โ AI calls: n2_qln_call(action: "exec", tool: "take_screenshot", args: {fullPage: true})
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
The AI only used `n2_qln_call`. It never saw the other 999 tools.
|
|
181
|
-
|
|
182
|
-
### 3-Stage Search Engine
|
|
183
|
-
|
|
184
|
-
QLN finds the right tool using three parallel search stages:
|
|
185
|
-
|
|
186
|
-
| Stage | Method | Speed | How it works |
|
|
187
|
-
|:---:|--------|:---:|------|
|
|
188
|
-
| **1** | Trigger Match |
|
|
189
|
-
| **2** | BM25 Keyword |
|
|
190
|
-
| **3** | Semantic Search |
|
|
191
|
-
|
|
192
|
-
Results from all stages are merged and ranked:
|
|
193
|
-
|
|
194
|
-
```
|
|
195
|
-
final_score = trigger_score ร 3.0
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
Tools that are used more often and succeed more reliably are ranked higher over time.
|
|
203
|
-
|
|
204
|
-
---
|
|
205
|
-
|
|
206
|
-
## API Reference
|
|
207
|
-
|
|
208
|
-
QLN exposes **one MCP tool** โ `n2_qln_call` โ with 5 actions.
|
|
209
|
-
|
|
210
|
-
### search โ Find tools by natural language
|
|
211
|
-
|
|
212
|
-
```javascript
|
|
213
|
-
n2_qln_call({
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
})
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
**Response:**
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
1. take_screenshot [capture] (score: 8.0)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
2. record_video [capture] (score: 5.2)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
### exec โ Execute a tool by name
|
|
235
|
-
|
|
236
|
-
```javascript
|
|
237
|
-
n2_qln_call({
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
})
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### create โ Register a new tool
|
|
248
|
-
|
|
249
|
-
```javascript
|
|
250
|
-
n2_qln_call({
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
})
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
**Validation rules (enforced โ rejected if violated):**
|
|
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
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
**Valid categories:** `web` ยท `data` ยท `file` ยท `dev` ยท `ai` ยท `capture` ยท `misc`
|
|
284
|
-
|
|
285
|
-
### update โ Modify an existing tool
|
|
286
|
-
|
|
287
|
-
```javascript
|
|
288
|
-
n2_qln_call({
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
})
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
Only changed fields need to be provided. Unchanged fields keep their current values. The same validation rules apply โ invalid updates are rejected.
|
|
298
|
-
|
|
299
|
-
### delete โ Remove tools
|
|
300
|
-
|
|
301
|
-
```javascript
|
|
302
|
-
// Delete a single tool by name
|
|
303
|
-
n2_qln_call({
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
// Delete ALL tools from a provider
|
|
309
|
-
n2_qln_call({
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
})
|
|
313
|
-
// โ
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
---
|
|
317
|
-
|
|
318
|
-
## Configuration
|
|
319
|
-
|
|
320
|
-
QLN works out of the box with zero configuration. To customize, create `config.local.js` in the QLN directory:
|
|
321
|
-
|
|
322
|
-
```javascript
|
|
323
|
-
module.exports = {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
};
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
> **Note:** `config.local.js` is gitignored. Your local settings won't be committed.
|
|
335
|
-
|
|
336
|
-
---
|
|
337
|
-
|
|
338
|
-
## Semantic Search Setup (Optional)
|
|
339
|
-
|
|
340
|
-
Without Ollama, QLN uses Stage 1 (trigger) + Stage 2 (keyword) matching, which already provides excellent results for most use cases.
|
|
341
|
-
|
|
342
|
-
For maximum accuracy, add semantic vector search (Stage 3):
|
|
343
|
-
|
|
344
|
-
### 1. Install Ollama
|
|
345
|
-
|
|
346
|
-
Download from [ollama.ai](https://ollama.ai) and install.
|
|
347
|
-
|
|
348
|
-
### 2. Pull the embedding model
|
|
349
|
-
|
|
350
|
-
```bash
|
|
351
|
-
ollama pull nomic-embed-text
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
### 3. Enable in config
|
|
355
|
-
|
|
356
|
-
Create `config.local.js`:
|
|
357
|
-
|
|
358
|
-
```javascript
|
|
359
|
-
module.exports = {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
};
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Comparison
|
|
370
|
-
|
|
371
|
-
| Setup | Search Stages | Accuracy | Dependencies |
|
|
372
|
-
|:------|:---:|:---:|:---:|
|
|
373
|
-
| **Default** (no Ollama) | Stage 1 + 2 |
|
|
374
|
-
| **With Ollama** | Stage 1 + 2 + 3 |
|
|
375
|
-
|
|
376
|
-
### Multilingual Users
|
|
377
|
-
|
|
378
|
-
`nomic-embed-text` is optimized for English. For **Korean, Japanese, Chinese**, or other languages, swap to a multilingual model:
|
|
379
|
-
|
|
380
|
-
```bash
|
|
381
|
-
ollama pull bge-m3
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
```javascript
|
|
385
|
-
// config.local.js
|
|
386
|
-
module.exports = {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
};
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
No code changes needed โ just swap the model name in config.
|
|
395
|
-
|
|
396
|
-
### Cloud Sync
|
|
397
|
-
|
|
398
|
-
Want your tool index synced across machines? Point `dataDir` to a cloud folder:
|
|
399
|
-
|
|
400
|
-
```javascript
|
|
401
|
-
// config.local.js
|
|
402
|
-
module.exports = {
|
|
403
|
-
|
|
404
|
-
};
|
|
405
|
-
```
|
|
406
|
-
|
|
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
|
-
---
|
|
410
|
-
|
|
411
|
-
## Project Structure
|
|
412
|
-
|
|
413
|
-
```
|
|
414
|
-
n2-qln/
|
|
415
|
-
โโโ index.js
|
|
416
|
-
โโโ lib/
|
|
417
|
-
โ
|
|
418
|
-
โ
|
|
419
|
-
โ
|
|
420
|
-
โ
|
|
421
|
-
โ
|
|
422
|
-
โ
|
|
423
|
-
โ
|
|
424
|
-
โ
|
|
425
|
-
โ
|
|
426
|
-
โ
|
|
427
|
-
โโโ tools/
|
|
428
|
-
โ
|
|
429
|
-
โโโ providers/
|
|
430
|
-
โโโ config.local.js
|
|
431
|
-
โโโ data/
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
## Tech Stack
|
|
435
|
-
|
|
436
|
-
| Component | Technology | Why |
|
|
437
|
-
|-----------|-----------|-----|
|
|
438
|
-
| 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, no build step |
|
|
440
|
-
| Embeddings | [Ollama](https://ollama.ai) + nomic-embed-text | Local, fast, free, optional |
|
|
441
|
-
| Protocol | [MCP](https://modelcontextprotocol.io) (Model Context Protocol) | Standard AI tool protocol |
|
|
442
|
-
| Validation | [Zod](https://zod.dev) | Runtime type-safe schema validation |
|
|
443
|
-
|
|
444
|
-
## Related Projects
|
|
445
|
-
|
|
446
|
-
| Project | Relationship |
|
|
447
|
-
|---------|-------------|
|
|
448
|
-
| [n2-soul](https://github.com/choihyunsus/soul) | AI agent orchestrator โ QLN serves as Soul's "tool brain" |
|
|
449
|
-
|
|
450
|
-
## Built & Battle-Tested
|
|
451
|
-
|
|
452
|
-
This isn't a weekend prototype. QLN has been **tested in production for 2+ months** and is actively used every day as the core tool router for [n2-soul](https://github.com/choihyunsus/soul).
|
|
453
|
-
|
|
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.
|
|
457
|
-
|
|
458
|
-
## FAQ
|
|
459
|
-
|
|
460
|
-
**"Why do you publish so many projects?"**
|
|
461
|
-
|
|
462
|
-
The N2 ecosystem has been in active development for over 4 months. Every project you see โ Soul, QLN, Ark โ has been built, tested, and validated in real daily workflows before being published. There's still more to come, not because we're spamming, but because there's a lot that's already been built and proven in production.
|
|
463
|
-
|
|
464
|
-
This is a solo developer project. Building, testing, and documenting everything alone takes time. Thank you for your patience and interest
|
|
465
|
-
|
|
466
|
-
## Contributing
|
|
467
|
-
|
|
468
|
-
Contributions are welcome! Here's how to get started:
|
|
469
|
-
|
|
470
|
-
1. Fork the repo
|
|
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
|
|
475
|
-
|
|
476
|
-
## Star History
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
## License
|
|
481
|
-
|
|
482
|
-
Apache-2.0
|
|
483
|
-
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
> *"1,000 tools in 200 tokens. That's not optimization โ that's a paradigm shift."*
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
<sub
|
|
1
|
+
๐ฐ๐ท [ํ๊ตญ์ด](README.ko.md)
|
|
2
|
+
|
|
3
|
+
# n2-qln
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/n2-qln) [](LICENSE) [](https://nodejs.org) [](https://www.npmjs.com/package/n2-qln)
|
|
6
|
+
|
|
7
|
+
**QLN** = **Q**uery **L**ayer **N**etwork โ a semantic search layer that sits between the AI and your tools.
|
|
8
|
+
|
|
9
|
+
> **Route 1,000+ tools through 1 MCP tool.** The AI sees only the router โ not all 1,000 tools.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
|
|
15
|
+
- [Features](#features)
|
|
16
|
+
- [The Problem](#the-problem)
|
|
17
|
+
- [Installation](#installation)
|
|
18
|
+
- [Setup](#setup)
|
|
19
|
+
- [How It Works](#how-it-works)
|
|
20
|
+
- [API Reference](#api-reference)
|
|
21
|
+
- [Configuration](#configuration)
|
|
22
|
+
- [Semantic Search Setup](#semantic-search-setup-optional)
|
|
23
|
+
- [Project Structure](#project-structure)
|
|
24
|
+
- [Built & Battle-Tested](#built--battle-tested)
|
|
25
|
+
- [FAQ](#faq)
|
|
26
|
+
- [Contributing](#contributing)
|
|
27
|
+
|
|
28
|
+
## Features
|
|
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.
|
|
31
|
+
|
|
32
|
+
**Sub-5ms search** โ 3-stage search engine (trigger + BM25 keyword + semantic) finds the right tool in under 5ms, even with 1,000+ tools indexed.
|
|
33
|
+
|
|
34
|
+
**BM25 keyword ranking** *(v3.4)* โ Stage 2 uses [Okapi BM25](https://en.wikipedia.org/wiki/Okapi_BM25) for keyword search. Rare terms score higher, document length is normalized. The same algorithm behind Google, Elasticsearch, and Wikipedia search.
|
|
35
|
+
|
|
36
|
+
**Self-learning ranking** โ Tools that get used more and succeed more are automatically ranked higher over time. No manual tuning needed.
|
|
37
|
+
|
|
38
|
+
**Live tool management** โ Add, update, or remove tools at runtime. No server restart required. Group tools by provider for bulk operations.
|
|
39
|
+
|
|
40
|
+
**Enforced quality** โ Strict validation on tool registration: `verb_target` naming, minimum description length, category constraints. Bad tools are rejected, not silently accepted.
|
|
41
|
+
|
|
42
|
+
**Semantic search (optional)** โ Add [Ollama](https://ollama.ai) for vector similarity search. Without it, Stage 1 + 2 still deliver great results. Graceful degradation โ if Ollama goes down, search keeps working.
|
|
43
|
+
|
|
44
|
+
**Zero native dependencies** โ Built on [sql.js](https://github.com/sql-js/sql.js) (WASM). No `node-gyp`, no build step, no platform-specific binaries. `npm install` and done.
|
|
45
|
+
|
|
46
|
+
**Dual execution** โ Tools can run as local functions or HTTP endpoints. Register a handler directly, or point to a remote service. Mix and match.
|
|
47
|
+
|
|
48
|
+
**Provider auto-indexing** *(v3.3)* โ Drop a JSON manifest in `providers/` and tools are auto-registered at boot. No code changes, no manual `create` calls. Idempotent and error-isolated.
|
|
49
|
+
|
|
50
|
+
**Scales to 10,000+** โ Centroid hierarchy partitions tools by category, then searches within partitions. 100 tools ~1ms, 1,000 ~3ms, 10,000 ~5ms.
|
|
51
|
+
|
|
52
|
+
**Universal MCP** โ Works with Claude Desktop, Cursor, n2-soul, or any MCP-compatible client. Standard stdio transport.
|
|
53
|
+
|
|
54
|
+
## The Problem
|
|
55
|
+
|
|
56
|
+
Every MCP tool you register eats AI context tokens. With 10 tools that's manageable. With 100, the AI slows down. **With 1,000, it's impossible** โ the context window is full before the conversation even starts.
|
|
57
|
+
|
|
58
|
+
QLN solves this by acting as a **semantic search router**:
|
|
59
|
+
|
|
60
|
+
1. Register all your tools in QLN's SQLite index
|
|
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**
|
|
63
|
+
|
|
64
|
+
**Result: ~200 tokens instead of ~50,000. 99.6% reduction.**
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install n2-qln
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Requirements:** Node.js โฅ 18
|
|
75
|
+
|
|
76
|
+
**Optional:** Install [Ollama](https://ollama.ai) for semantic vector search (Stage 3). See [Semantic Search Setup](#semantic-search-setup-optional).
|
|
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
|
|
85
|
+
|
|
86
|
+
Edit your Claude Desktop config file:
|
|
87
|
+
|
|
88
|
+
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
89
|
+
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"n2-qln": {
|
|
95
|
+
"command": "npx",
|
|
96
|
+
"args": ["-y", "n2-qln"]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Restart Claude Desktop. The `n2_qln_call` tool will appear in your tool list.
|
|
103
|
+
|
|
104
|
+
### Cursor
|
|
105
|
+
|
|
106
|
+
Open **Settings โ MCP Servers โ Add Server** and configure:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"name": "n2-qln",
|
|
111
|
+
"command": "npx",
|
|
112
|
+
"args": ["-y", "n2-qln"]
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### n2-soul
|
|
117
|
+
|
|
118
|
+
Add to your Soul `config.local.js`:
|
|
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:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
command: npx
|
|
150
|
+
args: ["-y", "n2-qln"]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Or if you cloned the repo:
|
|
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.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## How It Works
|
|
165
|
+
|
|
166
|
+
### Step-by-Step Example
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
User: "Take a screenshot of this page"
|
|
170
|
+
|
|
171
|
+
Step 1 โ AI calls: n2_qln_call(action: "search", query: "screenshot page")
|
|
172
|
+
QLN searches 1,000+ tools in <5ms
|
|
173
|
+
Response: take_screenshot (score: 8.0)
|
|
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
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The AI only used `n2_qln_call`. It never saw the other 999 tools.
|
|
181
|
+
|
|
182
|
+
### 3-Stage Search Engine
|
|
183
|
+
|
|
184
|
+
QLN finds the right tool using three parallel search stages:
|
|
185
|
+
|
|
186
|
+
| Stage | Method | Speed | How it works |
|
|
187
|
+
|:---:|--------|:---:|------|
|
|
188
|
+
| **1** | Trigger Match | <1ms | Matches exact words in tool names and trigger keywords |
|
|
189
|
+
| **2** | BM25 Keyword | 1-3ms | [Okapi BM25](https://en.wikipedia.org/wiki/Okapi_BM25) ranked search โ IDF weighting + document length normalization *(v3.4)* |
|
|
190
|
+
| **3** | Semantic Search | 5-15ms | Vector similarity using embeddings *(optional, requires Ollama)* |
|
|
191
|
+
|
|
192
|
+
Results from all stages are merged and ranked:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
final_score = trigger_score ร 3.0
|
|
196
|
+
+ bm25_keyword_score ร 1.0
|
|
197
|
+
+ semantic_score ร 2.0
|
|
198
|
+
+ log2(usage_count + 1) ร 0.5
|
|
199
|
+
+ success_rate ร 1.0
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Tools that are used more often and succeed more reliably are ranked higher over time.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## API Reference
|
|
207
|
+
|
|
208
|
+
QLN exposes **one MCP tool** โ `n2_qln_call` โ with 5 actions.
|
|
209
|
+
|
|
210
|
+
### search โ Find tools by natural language
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
n2_qln_call({
|
|
214
|
+
action: "search",
|
|
215
|
+
query: "take a screenshot", // natural language query (required)
|
|
216
|
+
category: "capture", // filter by category (optional)
|
|
217
|
+
topK: 5 // max results, default: 5 (optional)
|
|
218
|
+
})
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Response:**
|
|
222
|
+
```
|
|
223
|
+
Results for "take a screenshot" (3 found, 2ms):
|
|
224
|
+
|
|
225
|
+
1. take_screenshot [capture] (score: 8.0)
|
|
226
|
+
Take a full-page or viewport screenshot
|
|
227
|
+
Triggers: take_screenshot, screenshot, capture
|
|
228
|
+
|
|
229
|
+
2. record_video [capture] (score: 5.2)
|
|
230
|
+
Record browser video
|
|
231
|
+
Triggers: record_video, record, video
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### exec โ Execute a tool by name
|
|
235
|
+
|
|
236
|
+
```javascript
|
|
237
|
+
n2_qln_call({
|
|
238
|
+
action: "exec",
|
|
239
|
+
tool: "take_screenshot", // tool name (required)
|
|
240
|
+
args: { // tool arguments (optional)
|
|
241
|
+
fullPage: true,
|
|
242
|
+
format: "png"
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### create โ Register a new tool
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
250
|
+
n2_qln_call({
|
|
251
|
+
action: "create",
|
|
252
|
+
name: "read_pdf", // required, verb_target format
|
|
253
|
+
description: "Read and extract text from PDF files", // required, min 10 chars
|
|
254
|
+
category: "data", // required, see categories below
|
|
255
|
+
provider: "pdf-tools", // optional, groups tools by source
|
|
256
|
+
tags: ["pdf", "read", "extract", "document"], // optional, improves search
|
|
257
|
+
examples: [ // optional, indexed for keyword search
|
|
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
|
|
264
|
+
})
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Validation rules (enforced โ rejected if violated):**
|
|
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
|
|
286
|
+
|
|
287
|
+
```javascript
|
|
288
|
+
n2_qln_call({
|
|
289
|
+
action: "update",
|
|
290
|
+
tool: "read_pdf", // tool to update (required)
|
|
291
|
+
description: "Enhanced PDF text extractor", // any field can be updated
|
|
292
|
+
examples: ["read this PDF", "parse PDF"],
|
|
293
|
+
tags: ["pdf", "read", "parse"]
|
|
294
|
+
})
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Only changed fields need to be provided. Unchanged fields keep their current values. The same validation rules apply โ invalid updates are rejected.
|
|
298
|
+
|
|
299
|
+
### delete โ Remove tools
|
|
300
|
+
|
|
301
|
+
```javascript
|
|
302
|
+
// Delete a single tool by name
|
|
303
|
+
n2_qln_call({
|
|
304
|
+
action: "delete",
|
|
305
|
+
tool: "read_pdf"
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
// Delete ALL tools from a provider
|
|
309
|
+
n2_qln_call({
|
|
310
|
+
action: "delete",
|
|
311
|
+
provider: "pdf-tools"
|
|
312
|
+
})
|
|
313
|
+
// โ Deleted 3 tools from provider: pdf-tools
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Configuration
|
|
319
|
+
|
|
320
|
+
QLN works out of the box with zero configuration. To customize, create `config.local.js` in the QLN directory:
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
module.exports = {
|
|
324
|
+
dataDir: './data', // where SQLite DB is stored
|
|
325
|
+
embedding: {
|
|
326
|
+
enabled: true, // enable Stage 3 semantic search
|
|
327
|
+
provider: 'ollama',
|
|
328
|
+
model: 'nomic-embed-text',
|
|
329
|
+
baseUrl: 'http://127.0.0.1:11434',
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
> **Note:** `config.local.js` is gitignored. Your local settings won't be committed.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Semantic Search Setup (Optional)
|
|
339
|
+
|
|
340
|
+
Without Ollama, QLN uses Stage 1 (trigger) + Stage 2 (keyword) matching, which already provides excellent results for most use cases.
|
|
341
|
+
|
|
342
|
+
For maximum accuracy, add semantic vector search (Stage 3):
|
|
343
|
+
|
|
344
|
+
### 1. Install Ollama
|
|
345
|
+
|
|
346
|
+
Download from [ollama.ai](https://ollama.ai) and install.
|
|
347
|
+
|
|
348
|
+
### 2. Pull the embedding model
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
ollama pull nomic-embed-text
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### 3. Enable in config
|
|
355
|
+
|
|
356
|
+
Create `config.local.js`:
|
|
357
|
+
|
|
358
|
+
```javascript
|
|
359
|
+
module.exports = {
|
|
360
|
+
embedding: {
|
|
361
|
+
enabled: true,
|
|
362
|
+
provider: 'ollama',
|
|
363
|
+
model: 'nomic-embed-text',
|
|
364
|
+
baseUrl: 'http://127.0.0.1:11434',
|
|
365
|
+
},
|
|
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
|
+
|
|
376
|
+
### Multilingual Users
|
|
377
|
+
|
|
378
|
+
`nomic-embed-text` is optimized for English. For **Korean, Japanese, Chinese**, or other languages, swap to a multilingual model:
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
ollama pull bge-m3
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
```javascript
|
|
385
|
+
// config.local.js
|
|
386
|
+
module.exports = {
|
|
387
|
+
embedding: {
|
|
388
|
+
enabled: true,
|
|
389
|
+
model: 'bge-m3', // multilingual (100+ languages)
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
No code changes needed โ just swap the model name in config.
|
|
395
|
+
|
|
396
|
+
### Cloud Sync
|
|
397
|
+
|
|
398
|
+
Want your tool index synced across machines? Point `dataDir` to a cloud folder:
|
|
399
|
+
|
|
400
|
+
```javascript
|
|
401
|
+
// config.local.js
|
|
402
|
+
module.exports = {
|
|
403
|
+
dataDir: 'G:/My Drive/n2-qln', // Google Drive, OneDrive, Dropbox, NAS...
|
|
404
|
+
};
|
|
405
|
+
```
|
|
406
|
+
|
|
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
|
+
---
|
|
410
|
+
|
|
411
|
+
## Project Structure
|
|
412
|
+
|
|
413
|
+
```
|
|
414
|
+
n2-qln/
|
|
415
|
+
โโโ index.js # MCP server entry point
|
|
416
|
+
โโโ lib/
|
|
417
|
+
โ โโโ config.js # Config loader (merges default + local)
|
|
418
|
+
โ โโโ store.js # SQLite storage engine (sql.js WASM)
|
|
419
|
+
โ โโโ schema.js # Tool schema normalization + search text builder
|
|
420
|
+
โ โโโ validator.js # Enforced validation (name, description, category)
|
|
421
|
+
โ โโโ registry.js # Tool CRUD + usage tracking + embedding cache
|
|
422
|
+
โ โโโ router.js # 3-stage parallel search engine (BM25 v3.4)
|
|
423
|
+
โ โโโ vector-index.js # Float32 vector index with centroid hierarchy
|
|
424
|
+
โ โโโ embedding.js # Ollama embedding client (nomic-embed-text)
|
|
425
|
+
โ โโโ executor.js # HTTP/function tool executor
|
|
426
|
+
โ โโโ provider-loader.js # Auto-index providers/*.json at boot
|
|
427
|
+
โโโ tools/
|
|
428
|
+
โ โโโ qln-call.js # Unified MCP tool (search/exec/create/update/delete)
|
|
429
|
+
โโโ providers/ # Tool provider manifests (for bulk registration)
|
|
430
|
+
โโโ config.local.js # Local config overrides (gitignored)
|
|
431
|
+
โโโ data/ # SQLite database (gitignored, auto-created)
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Tech Stack
|
|
435
|
+
|
|
436
|
+
| Component | Technology | Why |
|
|
437
|
+
|-----------|-----------|-----|
|
|
438
|
+
| 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, no build step |
|
|
440
|
+
| Embeddings | [Ollama](https://ollama.ai) + nomic-embed-text | Local, fast, free, optional |
|
|
441
|
+
| Protocol | [MCP](https://modelcontextprotocol.io) (Model Context Protocol) | Standard AI tool protocol |
|
|
442
|
+
| Validation | [Zod](https://zod.dev) | Runtime type-safe schema validation |
|
|
443
|
+
|
|
444
|
+
## Related Projects
|
|
445
|
+
|
|
446
|
+
| Project | Relationship |
|
|
447
|
+
|---------|-------------|
|
|
448
|
+
| [n2-soul](https://github.com/choihyunsus/soul) | AI agent orchestrator โ QLN serves as Soul's "tool brain" |
|
|
449
|
+
|
|
450
|
+
## Built & Battle-Tested
|
|
451
|
+
|
|
452
|
+
This isn't a weekend prototype. QLN has been **tested in production for 2+ months** and is actively used every day as the core tool router for [n2-soul](https://github.com/choihyunsus/soul).
|
|
453
|
+
|
|
454
|
+
Written by **Rose** โ N2's first AI agent, and the one who routes through QLN hundreds of times a day.
|
|
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.
|
|
457
|
+
|
|
458
|
+
## FAQ
|
|
459
|
+
|
|
460
|
+
**"Why do you publish so many projects?"**
|
|
461
|
+
|
|
462
|
+
The N2 ecosystem has been in active development for over 4 months. Every project you see โ Soul, QLN, Ark โ has been built, tested, and validated in real daily workflows before being published. There's still more to come, not because we're spamming, but because there's a lot that's already been built and proven in production.
|
|
463
|
+
|
|
464
|
+
This is a solo developer project. Building, testing, and documenting everything alone takes time. Thank you for your patience and interest
|
|
465
|
+
|
|
466
|
+
## Contributing
|
|
467
|
+
|
|
468
|
+
Contributions are welcome! Here's how to get started:
|
|
469
|
+
|
|
470
|
+
1. Fork the repo
|
|
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
|
|
475
|
+
|
|
476
|
+
## Star History
|
|
477
|
+
|
|
478
|
+
No coffee? A star is fine too โ
|
|
479
|
+
|
|
480
|
+
## License
|
|
481
|
+
|
|
482
|
+
Apache-2.0
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
> *"1,000 tools in 200 tokens. That's not optimization โ that's a paradigm shift."*
|
|
487
|
+
|
|
488
|
+
[nton2.com](https://nton2.com) ยท [npm](https://www.npmjs.com/package/n2-qln) ยท lagi0730@gmail.com
|
|
489
|
+
|
|
490
|
+
<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>
|