# How FTS5 Full-Text Search Enables Instant Node Discovery in n8n-MCP

> Discover n8n nodes instantly with FTS5 full-text search. n8n-MCP indexes nodes for sub-20ms typo-tolerant searches across thousands of workflows.

- Repository: [Romuald Członkowski/n8n-mcp](https://github.com/czlonkowski/n8n-mcp)
- Tags: deep-dive
- Published: 2026-03-24

---

**n8n-MCP uses a SQLite FTS5 virtual table to index every n8n node's textual metadata, enabling sub-20ms full-text searches across thousands of core and community nodes with typo-tolerant fallback modes.**

The n8n-MCP server acts as a bridge between AI assistants and the n8n automation platform, maintaining a comprehensive catalog of available integration nodes. To deliver instant answers to "find the node that does X" queries without scanning thousands of rows linearly, the project implements **FTS5 full-text search** via SQLite's virtual table mechanism, eliminating the performance penalties of traditional `LIKE` pattern matching.

## The Database Architecture: Virtual Tables and Triggers

### Creating the nodes_fts Virtual Table

In [`src/services/node-documentation-service.ts`](https://github.com/czlonkowski/n8n-mcp/blob/main/src/services/node-documentation-service.ts) (lines 191-203), the service defines a virtual table that shadows the main `nodes` table:

```sql
CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
  node_type,
  name,
  display_name,
  description,
  category,
  documentation_markdown,
  aliases,
  content=nodes,
  content_rowid=id
);

```

The `content=nodes` and `content_rowid=id` directives link the FTS5 index to the source table, ensuring the virtual table references the original row data without duplication while maintaining a separate inverted index for text tokens.

### Automatic Index Maintenance with Triggers

To keep the search index synchronized, the same file (lines 206-222) declares triggers that propagate changes from the main table:

- **INSERT**: Adds new rows to `nodes_fts` immediately upon insertion into `nodes`
- **DELETE**: Removes corresponding entries from the FTS5 index
- **UPDATE**: Replaces existing FTS5 records to reflect modified node metadata

This trigger-based approach ensures the **full-text index** remains consistent with the node catalog without requiring manual re-indexing or batch updates.

## Runtime Search Implementation

### The searchNodes Dispatcher Method

When the MCP server's `search_nodes` tool receives a query, the `searchNodes` method in [`src/mcp/server.ts`](https://github.com/czlonkowski/n8n-mcp/blob/main/src/mcp/server.ts) (lines 1661-1670) first verifies FTS5 availability:

```typescript
const ftsExists = this.db.prepare(`
  SELECT name FROM sqlite_master 
  WHERE type='table' AND name='nodes_fts'
`).get();

if (ftsExists) {
  return this.searchNodesFTS(normalizedQuery, limit, searchMode, options);
} else {
  return this.searchNodesLIKE(normalizedQuery, limit, options);
}

```

If the virtual table exists, the engine routes to `searchNodesFTS`; otherwise, it degrades gracefully to the slower `searchNodesLIKE` implementation.

### Constructing Ranked MATCH Queries

The core FTS5 implementation (lines 1675-1695) builds a boolean query and applies custom relevance scoring:

```typescript
const nodes = this.db.prepare(`
  SELECT n.*, rank
  FROM nodes n
  JOIN nodes_fts ON n.rowid = nodes_fts.rowid
  WHERE nodes_fts MATCH ?
  ${sourceFilter}
  ORDER BY
    CASE
      WHEN LOWER(n.display_name) = LOWER(?) THEN 0
      WHEN LOWER(n.display_name) LIKE LOWER(?) THEN 1
      WHEN LOWER(n.node_type) LIKE LOWER(?) THEN 2
      ELSE 3
    END,
    rank,
    n.display_name
  LIMIT ?
`).all(ftsQuery, cleanedQuery, `%${cleanedQuery}%`, `%${cleanedQuery}%`, limit);

```

This query prioritizes exact display name matches, then partial matches, then node type matches, finally falling back to the FTS5 internal `rank` value. The system specifically promotes popular core nodes—including **HTTP Request**, **Webhook**, **Set**, **Code**, and **Slack**—to ensure frequently-used integrations surface first in result sets.

## Search Modes and Source Filtering

The implementation supports three distinct search behaviors via the `mode` parameter:

- **OR mode** (default): Treats query terms as alternatives, returning nodes matching any keyword using FTS5's implicit OR operator
- **AND mode**: Requires all terms to appear, constructing boolean expressions (`term1 AND term2`) for the MATCH clause
- **FUZZY mode**: Detects potential misspellings by first attempting an FTS5 search, then falling back to `LIKE` patterns with wildcards if no results return

**Source filters** (`core`, `community`, `verified`) append cheap SQL predicates to the `WHERE` clause, restricting results without rebuilding the query plan or affecting index utilization.

## Initial Setup and Migration

For fresh installations or rebuilds, [`scripts/migrate-nodes-fts.ts`](https://github.com/czlonkowski/n8n-mcp/blob/main/scripts/migrate-nodes-fts.ts) (lines 30-51) creates the virtual table and bulk-populates it from existing node rows:

```typescript
// Migration logic creates the virtual table and inserts existing nodes
db.exec(`
  INSERT INTO nodes_fts(node_type, name, display_name, description, category, documentation_markdown, aliases)
  SELECT node_type, name, display_name, description, category, documentation_markdown, aliases
  FROM nodes
`);

```

This migration runs once during setup; thereafter, the triggers maintain synchronization automatically as the catalog updates.

## Practical Code Examples

The following examples demonstrate how to query the node catalog using the MCP tool interface:

**Basic keyword search** using default OR mode:

```typescript
await client.callTool('search_nodes', { query: 'webhook' });
// Returns the Webhook node as the top result

```

**Exact phrase matching** with AND mode:

```typescript
await client.callTool('search_nodes', {
  query: '"google sheets"',
  mode: 'AND',
});
// Only nodes containing both "google" and "sheets" in indexed fields

```

**Typo-tolerant search** using FUZZY mode:

```typescript
await client.callTool('search_nodes', {
  query: 'slak',
  mode: 'FUZZY',
});
// Finds the Slack node despite the misspelling via LIKE fallback

```

**Filtered community search** with result limits:

```typescript
await client.callTool('search_nodes', {
  query: 'scraping',
  source: 'community',
  limit: 5,
});
// Returns 5 most relevant community scraping integrations

```

**Raw SQLite access** for debugging or custom analytics:

```typescript
const db = await getDatabase(); // MCP-provided SQLite handle
const rows = db.prepare(`
  SELECT n.node_type, n.display_name, rank
  FROM nodes n
  JOIN nodes_fts ON n.rowid = nodes_fts.rowid
  WHERE nodes_fts MATCH ?
  ORDER BY rank LIMIT 10
`).all('http request');

```

## Key Implementation Files

- **[`src/services/node-documentation-service.ts`](https://github.com/czlonkowski/n8n-mcp/blob/main/src/services/node-documentation-service.ts)**: Defines the `nodes_fts` virtual table schema and maintenance triggers (lines 191-222)
- **[`src/mcp/server.ts`](https://github.com/czlonkowski/n8n-mcp/blob/main/src/mcp/server.ts)**: Implements the `searchNodes` dispatcher and `searchNodesFTS` method with ranked MATCH queries (lines 1661-1695)
- **[`scripts/migrate-nodes-fts.ts`](https://github.com/czlonkowski/n8n-mcp/blob/main/scripts/migrate-nodes-fts.ts)**: Handles initial creation and population of the FTS5 table (lines 30-51)
- **[`tests/integration/database/node-fts5-search.test.ts`](https://github.com/czlonkowski/n8n-mcp/blob/main/tests/integration/database/node-fts5-search.test.ts)**: Validates trigger behavior and search accuracy

## Summary

- **FTS5 virtual tables** in SQLite provide the foundation for sub-20ms node discovery across the entire n8n catalog according to the czlonkowski/n8n-mcp source code
- **Trigger-based synchronization** automatically maintains the search index as nodes are inserted, updated, or deleted without manual intervention
- **Intelligent fallback logic** routes queries to `searchNodesFTS` when available, degrading gracefully to `LIKE` searches only when the virtual table is absent
- **Multi-tier ranking** combines exact-match priority, FTS5 relevance scores, and popularity boosting for core nodes like HTTP Request and Webhook
- **Flexible search modes** support OR, AND, and FUZZY queries with optional source filtering for core, community, or verified nodes

## Frequently Asked Questions

### What makes FTS5 faster than LIKE queries for node discovery?

FTS5 uses **inverted indices** to map search terms directly to row locations, avoiding full-table scans. While `LIKE '%term%'` checks every row sequentially, the MATCH query in [`src/mcp/server.ts`](https://github.com/czlonkowski/n8n-mcp/blob/main/src/mcp/server.ts) leverages the pre-built index to locate relevant nodes in logarithmic time, consistently delivering results under 20 milliseconds even across thousands of community nodes.

### How does n8n-mcp handle typos in search queries?

When `mode: 'FUZZY'` is specified, the system first attempts an FTS5 MATCH query. If no results return, it automatically falls back to a `LIKE` pattern search with wildcards, as implemented in the `searchNodes` dispatcher. This dual-path approach ensures users find the intended node—such as locating "Slack" when typing "slak"—without requiring exact spelling.

### Can I restrict searches to official n8n core nodes only?

Yes. The `source` parameter accepts `'core'`, `'community'`, or `'verified'` values, which append SQL predicates to the WHERE clause in the MATCH query. Setting `source: 'core'` filters the result set to official n8n nodes before the ranking algorithm applies, ensuring only maintained integrations appear in results.

### What happens if the FTS5 table is missing or corrupted?

The `searchNodes` method in [`src/mcp/server.ts`](https://github.com/czlonkowski/n8n-mcp/blob/main/src/mcp/server.ts) explicitly checks for the `nodes_fts` table existence before routing to `searchNodesFTS`. If the table is absent—such as after a fresh clone without running migrations—the system routes requests to `searchNodesLIKE`, which uses standard SQL pattern matching. Administrators can rebuild the index by executing [`scripts/migrate-nodes-fts.ts`](https://github.com/czlonkowski/n8n-mcp/blob/main/scripts/migrate-nodes-fts.ts) to restore full-text performance.