n2-qln 3.4.0 โ†’ 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.md CHANGED
@@ -1,490 +1,490 @@
1
- ๐Ÿ‡ฐ๐Ÿ‡ท [ํ•œ๊ตญ์–ด](README.ko.md)
2
-
3
- # n2-qln
4
-
5
- [![npm](https://img.shields.io/npm/v/n2-qln?color=brightgreen)](https://www.npmjs.com/package/n2-qln) [![license](https://img.shields.io/npm/l/n2-qln)](LICENSE) [![node](https://img.shields.io/node/v/n2-qln?color=brightgreen)](https://nodejs.org) [![downloads](https://img.shields.io/npm/dm/n2-qln?color=blue)](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
- ![QLN Architecture โ€” Without vs With](docs/architecture.png)
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
- If you find QLN helpful, please consider giving us a star! โญ
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>
1
+ ๐Ÿ‡ฐ๐Ÿ‡ท [ํ•œ๊ตญ์–ด](README.ko.md)
2
+
3
+ # n2-qln
4
+
5
+ [![npm](https://img.shields.io/npm/v/n2-qln?color=brightgreen)](https://www.npmjs.com/package/n2-qln) [![license](https://img.shields.io/npm/l/n2-qln)](LICENSE) [![node](https://img.shields.io/node/v/n2-qln?color=brightgreen)](https://nodejs.org) [![downloads](https://img.shields.io/npm/dm/n2-qln?color=blue)](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
+ ![QLN Architecture โ€” Without vs With](docs/architecture.png)
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>