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.
Files changed (57) hide show
  1. package/README.ko.md +251 -262
  2. package/README.md +245 -276
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.js +87 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/lib/config.d.ts +9 -0
  7. package/{lib → dist/lib}/config.js +23 -27
  8. package/dist/lib/config.js.map +1 -0
  9. package/dist/lib/embedding.d.ts +27 -0
  10. package/{lib → dist/lib}/embedding.js +39 -47
  11. package/dist/lib/embedding.js.map +1 -0
  12. package/dist/lib/executor.d.ts +57 -0
  13. package/dist/lib/executor.js +175 -0
  14. package/dist/lib/executor.js.map +1 -0
  15. package/dist/lib/mcp-discovery.d.ts +83 -0
  16. package/dist/lib/mcp-discovery.js +203 -0
  17. package/dist/lib/mcp-discovery.js.map +1 -0
  18. package/dist/lib/provider-loader.d.ts +13 -0
  19. package/dist/lib/provider-loader.js +146 -0
  20. package/dist/lib/provider-loader.js.map +1 -0
  21. package/dist/lib/registry.d.ts +38 -0
  22. package/{lib → dist/lib}/registry.js +82 -92
  23. package/dist/lib/registry.js.map +1 -0
  24. package/dist/lib/router.d.ts +63 -0
  25. package/{lib → dist/lib}/router.js +75 -117
  26. package/dist/lib/router.js.map +1 -0
  27. package/dist/lib/schema.d.ts +20 -0
  28. package/{lib → dist/lib}/schema.js +38 -30
  29. package/dist/lib/schema.js.map +1 -0
  30. package/dist/lib/store.d.ts +37 -0
  31. package/dist/lib/store.js +207 -0
  32. package/dist/lib/store.js.map +1 -0
  33. package/dist/lib/validator.d.ts +37 -0
  34. package/dist/lib/validator.js +114 -0
  35. package/dist/lib/validator.js.map +1 -0
  36. package/dist/lib/vector-index.d.ts +37 -0
  37. package/{lib → dist/lib}/vector-index.js +19 -36
  38. package/dist/lib/vector-index.js.map +1 -0
  39. package/dist/tools/qln-call.d.ts +41 -0
  40. package/dist/tools/qln-call.js +353 -0
  41. package/dist/tools/qln-call.js.map +1 -0
  42. package/dist/tools/qln-helpers.d.ts +55 -0
  43. package/dist/tools/qln-helpers.js +88 -0
  44. package/dist/tools/qln-helpers.js.map +1 -0
  45. package/dist/types.d.ts +243 -0
  46. package/dist/types.js +4 -0
  47. package/dist/types.js.map +1 -0
  48. package/index.js +3 -79
  49. package/package.json +12 -5
  50. package/.github/FUNDING.yml +0 -3
  51. package/docs/README.md +0 -2
  52. package/docs/architecture.png +0 -0
  53. package/lib/executor.js +0 -104
  54. package/lib/provider-loader.js +0 -126
  55. package/lib/store.js +0 -217
  56. package/lib/validator.js +0 -171
  57. package/tools/qln-call.js +0 -257
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
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
6
 
7
- **QLN** = **Q**uery **L**ayer **N**etwork — a semantic search layer that sits between the AI and your tools.
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
- - [Features](#features)
16
- - [The Problem](#the-problem)
17
- - [Installation](#installation)
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
- ## 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.
27
+ ## Why QLN
31
28
 
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.
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
- 🎯 **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.
31
+ QLN solves this:
35
32
 
36
- 📈 **Self-learning ranking** — Tools that get used more and succeed more are automatically ranked higher over time. No manual tuning needed.
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
- 🔄 **Live tool management** Add, update, or remove tools at runtime. No server restart required. Group tools by provider for bulk operations.
37
+ **Result: ~200 tokens instead of ~50,000. 99.6% reduction.**
39
38
 
40
- 🛡️ **Enforced quality** — Strict validation on tool registration: `verb_target` naming, minimum description length, category constraints. Bad tools are rejected, not silently accepted.
39
+ ## Features
41
40
 
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.
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
- 📦 **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.
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
- 🔌 **Dual execution** — Tools can run as local functions or HTTP endpoints. Register a handler directly, or point to a remote service. Mix and match.
75
+ ### Circuit Breaker
47
76
 
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.
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
- 🏗️ **Scales to 10,000+** — Centroid hierarchy partitions tools by category, then searches within partitions. 100 tools ~1ms, 1,000 ~3ms, 10,000 ~5ms.
79
+ ```
80
+ closed → 3 failures → open (fast-fail) → 60s → half-open (retry) → success → closed
81
+ ```
51
82
 
52
- 🌍 **Universal MCP** — Works with Claude Desktop, Cursor, n2-soul, or any MCP-compatible client. Standard stdio transport.
83
+ ### 🔄 Fallback Chain
53
84
 
54
- ## The Problem
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
- 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.
87
+ ```
88
+ auto "send notification" → try push_notification ❌ → try send_email ✅
89
+ ```
57
90
 
58
- QLN solves this by acting as a **semantic search router**:
91
+ ### 🎯 Boost Keywords
59
92
 
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**
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
- **Result: ~200 tokens instead of ~50,000. 99.6% reduction.**
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
- ## Installation
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
- **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
113
+ ### Connect to an MCP Client
85
114
 
86
- Edit your Claude Desktop config file:
115
+ <details>
116
+ <summary><strong>Claude Desktop</strong></summary>
87
117
 
88
- - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
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
- Restart Claude Desktop. The `n2_qln_call` tool will appear in your tool list.
132
+ <details>
133
+ <summary><strong>Cursor</strong></summary>
103
134
 
104
- ### Cursor
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
- ### n2-soul
146
+ <details>
147
+ <summary><strong>Any MCP Client</strong></summary>
117
148
 
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:
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
- 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.
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
- 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
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
- QLN finds the right tool using three parallel search stages:
185
-
186
- | Stage | Method | Speed | How it works |
173
+ | Stage | Method | Speed | Details |
187
174
  |:---:|--------|:---:|------|
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)* |
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 from all stages are merged and ranked:
179
+ Results are merged and ranked:
193
180
 
194
181
  ```
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
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 5 actions.
190
+ QLN exposes **one MCP tool** — `n2_qln_call` — with 9 actions.
191
+
192
+ ### auto — Search + Execute (one-shot)
209
193
 
210
- ### search Find tools by natural language
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: "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)
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
- **Response:**
222
- ```
223
- 🔍 Results for "take a screenshot" (3 found, 2ms):
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
- 1. take_screenshot [capture] (score: 8.0)
226
- Take a full-page or viewport screenshot
227
- Triggers: take_screenshot, screenshot, capture
209
+ ### search Find tools
228
210
 
229
- 2. record_video [capture] (score: 5.2)
230
- Record browser video
231
- Triggers: record_video, record, video
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 by name
219
+ ### exec — Execute a specific tool
235
220
 
236
221
  ```javascript
237
222
  n2_qln_call({
238
223
  action: "exec",
239
- tool: "take_screenshot", // tool name (required)
240
- args: { // tool arguments (optional)
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 new tool
229
+ ### create — Register a tool
248
230
 
249
231
  ```javascript
250
232
  n2_qln_call({
251
233
  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
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
- **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
243
+ ### injectBulk register
286
244
 
287
245
  ```javascript
288
246
  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"]
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
- Only changed fields need to be provided. Unchanged fields keep their current values. The same validation rules apply invalid updates are rejected.
256
+ ### discoverScan MCP servers
298
257
 
299
- ### delete — Remove tools
258
+ See [MCP Auto-Discovery](#mcp-auto-discovery).
259
+
260
+ ### update / delete / stats
300
261
 
301
262
  ```javascript
302
- // Delete a single tool by name
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 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
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
- ## Configuration
276
+ ## MCP Auto-Discovery
319
277
 
320
- QLN works out of the box with zero configuration. To customize, create `config.local.js` in the QLN directory:
278
+ The killer feature of v4.1. Connect any MCP server and QLN auto-indexes all its tools.
321
279
 
322
280
  ```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
- };
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
- > **Note:** `config.local.js` is gitignored. Your local settings won't be committed.
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
- ## Semantic Search Setup (Optional)
297
+ **Re-discovery is idempotent** run it again and old entries are purged before re-registering.
339
298
 
340
- Without Ollama, QLN uses Stage 1 (trigger) + Stage 2 (keyword) matching, which already provides excellent results for most use cases.
299
+ ---
341
300
 
342
- For maximum accuracy, add semantic vector search (Stage 3):
301
+ ## Provider Manifests
343
302
 
344
- ### 1. Install Ollama
303
+ Drop a JSON file in `providers/` and tools are auto-indexed at boot. No code changes, no manual calls.
345
304
 
346
- Download from [ollama.ai](https://ollama.ai) and install.
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
- ### 2. Pull the embedding model
321
+ Hot reload: edit a manifest while QLN is running — changes are picked up automatically.
349
322
 
350
- ```bash
351
- ollama pull nomic-embed-text
352
- ```
323
+ ---
353
324
 
354
- ### 3. Enable in config
325
+ ## Configuration
355
326
 
356
- Create `config.local.js`:
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
- ### Multilingual Users
377
-
378
- `nomic-embed-text` is optimized for English. For **Korean, Japanese, Chinese**, or other languages, swap to a multilingual model:
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
- ```bash
381
- ollama pull bge-m3
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
- ```javascript
385
- // config.local.js
386
- module.exports = {
387
- embedding: {
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
- No code changes needed just swap the model name in config.
368
+ > `config.local.js` is gitignored. Cloud sync: point `dataDir` to Google Drive / OneDrive / NAS.
395
369
 
396
- ### Cloud Sync
370
+ ### Semantic Search (Optional)
397
371
 
398
- Want your tool index synced across machines? Point `dataDir` to a cloud folder:
372
+ Without Ollama, Stage 1 + 2 already deliver great results.
399
373
 
400
- ```javascript
401
- // config.local.js
402
- module.exports = {
403
- dataDir: 'G:/My Drive/n2-qln', // Google Drive, OneDrive, Dropbox, NAS...
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
- ├── 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)
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, 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 |
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 serves as Soul's "tool brain" |
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
- 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).
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** 🌹 — 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.
426
+ Written by **Rose** — N2's first AI agent.
457
427
 
458
428
  ## FAQ
459
429
 
460
- **"Why do you publish so many projects?"**
430
+ **"Why one tool instead of many?"**
461
431
 
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.
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
- This is a solo developer project. Building, testing, and documenting everything alone takes time. Thank you for your patience and interest 🙏
434
+ **"What if the search picks the wrong tool?"**
465
435
 
466
- ## Contributing
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
- Contributions are welcome! Here's how to get started:
438
+ **"Do I need Ollama?"**
469
439
 
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
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
- ## Star History
442
+ ## Contributing
477
443
 
478
- If you find QLN helpful, please consider giving us a star! ⭐
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
- 🌐 [nton2.com](https://nton2.com) · 📦 [npm](https://www.npmjs.com/package/n2-qln) · ✉️ lagi0730@gmail.com
457
+ 🔗 [nton2.com](https://nton2.com) · [npm](https://www.npmjs.com/package/n2-qln) · lagi0730@gmail.com
489
458
 
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>
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>