doc2vec 1.1.0 → 1.1.1

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
@@ -9,6 +9,7 @@ The primary goal is to prepare documentation content for Retrieval-Augmented Gen
9
9
  ## Key Features
10
10
 
11
11
  * **Website Crawling:** Recursively crawls websites starting from a given base URL.
12
+ * **Sitemap Support:** Extracts URLs from XML sitemaps to discover pages not linked in navigation.
12
13
  * **GitHub Issues Integration:** Retrieves GitHub issues and comments, processing them into searchable chunks.
13
14
  * **Local Directory Processing:** Scans local directories for files, converts content to searchable chunks.
14
15
  * **Content Extraction:** Uses Puppeteer for rendering JavaScript-heavy pages and `@mozilla/readability` to extract the main article content.
@@ -79,6 +80,7 @@ Configuration is managed through two files:
79
80
 
80
81
  For websites (`type: 'website'`):
81
82
  * `url`: The starting URL for crawling the documentation site.
83
+ * `sitemap_url`: (Optional) URL to the site's XML sitemap for discovering additional pages not linked in navigation.
82
84
 
83
85
  For GitHub repositories (`type: 'github'`):
84
86
  * `repo`: Repository name in the format `'owner/repo'` (e.g., `'istio/istio'`).
@@ -114,6 +116,7 @@ Configuration is managed through two files:
114
116
  product_name: 'argo'
115
117
  version: 'stable'
116
118
  url: 'https://argo-cd.readthedocs.io/en/stable/'
119
+ sitemap_url: 'https://argo-cd.readthedocs.io/en/stable/sitemap.xml'
117
120
  max_size: 1048576
118
121
  database_config:
119
122
  type: 'sqlite'
@@ -180,7 +183,7 @@ The script will then:
180
183
  3. Iterate through each source defined in the config.
181
184
  4. Initialize the specified database connection.
182
185
  5. Process each source according to its type:
183
- - For websites: Crawl the site, extract content, convert to Markdown
186
+ - For websites: Crawl the site, process any sitemaps, extract content, convert to Markdown
184
187
  - For GitHub repos: Fetch issues and comments, convert to Markdown
185
188
  - For local directories: Scan files, process content (converting HTML to Markdown if needed)
186
189
  6. For all sources: Chunk content, check for changes, generate embeddings (if needed), and store/update in the database.
@@ -214,7 +217,7 @@ This will:
214
217
 
215
218
  3. Generate, embed, and store documentation chunks in the configured database(s).
216
219
 
217
- If you dont specify a config path, it will look for config.yaml in the current working directory.
220
+ If you don't specify a config path, it will look for config.yaml in the current working directory.
218
221
 
219
222
  ## Core Logic Flow
220
223
 
@@ -225,6 +228,7 @@ If you don’t specify a config path, it will look for config.yaml in the curren
225
228
  2. **Process by Source Type:**
226
229
  - **For Websites:**
227
230
  * Start at the base `url`.
231
+ * If `sitemap_url` is provided, fetch and parse the sitemap to extract additional URLs.
228
232
  * Use Puppeteer (`processPage`) to fetch and render HTML.
229
233
  * Use Readability to extract main content.
230
234
  * Sanitize HTML.
package/config.yaml CHANGED
@@ -77,6 +77,7 @@ sources:
77
77
  product_name: 'gloo-mesh-core'
78
78
  version: 'latest'
79
79
  url: 'https://docs.solo.io/gloo-mesh/latest/'
80
+ sitemap_url: 'https://docs.solo.io/gloo-mesh/sitemap.xml'
80
81
  max_size: 1048576
81
82
  database_config:
82
83
  type: 'sqlite'
@@ -87,6 +88,7 @@ sources:
87
88
  product_name: 'gloo-mesh-enterprise'
88
89
  version: 'latest'
89
90
  url: 'https://docs.solo.io/gloo-mesh-enterprise/latest/'
91
+ sitemap_url: 'https://docs.solo.io/gloo-mesh-enterprise/sitemap.xml'
90
92
  max_size: 1048576
91
93
  database_config:
92
94
  type: 'sqlite'
@@ -107,6 +109,7 @@ sources:
107
109
  product_name: 'gloo-gateway'
108
110
  version: 'latest'
109
111
  url: 'https://docs.solo.io/gateway/latest/'
112
+ sitemap_url: 'https://docs.solo.io/gateway/sitemap.xml'
110
113
  max_size: 1048576
111
114
  database_config:
112
115
  type: 'sqlite'
package/dist/doc2vec.js CHANGED
@@ -1158,10 +1158,58 @@ class Doc2Vec {
1158
1158
  logger.error(`Error removing obsolete chunks from Qdrant:`, error);
1159
1159
  }
1160
1160
  }
1161
+ async parseSitemap(sitemapUrl, logger) {
1162
+ logger.info(`Parsing sitemap from ${sitemapUrl}`);
1163
+ try {
1164
+ const response = await axios_1.default.get(sitemapUrl);
1165
+ const $ = (0, cheerio_1.load)(response.data, { xmlMode: true });
1166
+ const urls = [];
1167
+ // Handle standard sitemaps
1168
+ $('url > loc').each((_, element) => {
1169
+ const url = $(element).text().trim();
1170
+ if (url) {
1171
+ urls.push(url);
1172
+ }
1173
+ });
1174
+ // Handle sitemap indexes (sitemaps that link to other sitemaps)
1175
+ const sitemapLinks = [];
1176
+ $('sitemap > loc').each((_, element) => {
1177
+ const nestedSitemapUrl = $(element).text().trim();
1178
+ if (nestedSitemapUrl) {
1179
+ sitemapLinks.push(nestedSitemapUrl);
1180
+ }
1181
+ });
1182
+ // Recursively process nested sitemaps
1183
+ for (const nestedSitemapUrl of sitemapLinks) {
1184
+ logger.debug(`Found nested sitemap: ${nestedSitemapUrl}`);
1185
+ const nestedUrls = await this.parseSitemap(nestedSitemapUrl, logger);
1186
+ urls.push(...nestedUrls);
1187
+ }
1188
+ logger.info(`Found ${urls.length} URLs in sitemap ${sitemapUrl}`);
1189
+ return urls;
1190
+ }
1191
+ catch (error) {
1192
+ logger.error(`Error parsing sitemap at ${sitemapUrl}:`, error);
1193
+ return [];
1194
+ }
1195
+ }
1161
1196
  async crawlWebsite(baseUrl, sourceConfig, processPageContent, parentLogger, visitedUrls) {
1162
1197
  const logger = parentLogger.child('crawler');
1163
1198
  const queue = [baseUrl];
1164
- logger.info(`Starting crawl from ${baseUrl}`);
1199
+ // Process sitemap if provided
1200
+ if (sourceConfig.sitemap_url) {
1201
+ logger.section('SITEMAP PROCESSING');
1202
+ const sitemapUrls = await this.parseSitemap(sourceConfig.sitemap_url, logger);
1203
+ // Add sitemap URLs to the queue if they're within the website scope
1204
+ for (const url of sitemapUrls) {
1205
+ if (url.startsWith(sourceConfig.url) && !queue.includes(url)) {
1206
+ logger.debug(`Adding URL from sitemap to queue: ${url}`);
1207
+ queue.push(url);
1208
+ }
1209
+ }
1210
+ logger.info(`Added ${queue.length - 1} URLs from sitemap to the crawl queue`);
1211
+ }
1212
+ logger.info(`Starting crawl from ${baseUrl} with ${queue.length} URLs in initial queue`);
1165
1213
  let processedCount = 0;
1166
1214
  let skippedCount = 0;
1167
1215
  let skippedSizeCount = 0;
package/doc2vec.ts CHANGED
@@ -50,6 +50,7 @@ interface LocalDirectorySourceConfig extends BaseSourceConfig {
50
50
  interface WebsiteSourceConfig extends BaseSourceConfig {
51
51
  type: 'website';
52
52
  url: string;
53
+ sitemap_url?: string; // Optional sitemap URL to extract additional URLs to crawl
53
54
  }
54
55
 
55
56
  // Configuration specific to GitHub repo sources
@@ -1364,6 +1365,46 @@ class Doc2Vec {
1364
1365
  }
1365
1366
  }
1366
1367
 
1368
+ private async parseSitemap(sitemapUrl: string, logger: Logger): Promise<string[]> {
1369
+ logger.info(`Parsing sitemap from ${sitemapUrl}`);
1370
+ try {
1371
+ const response = await axios.get(sitemapUrl);
1372
+ const $ = load(response.data, { xmlMode: true });
1373
+
1374
+ const urls: string[] = [];
1375
+
1376
+ // Handle standard sitemaps
1377
+ $('url > loc').each((_, element) => {
1378
+ const url = $(element).text().trim();
1379
+ if (url) {
1380
+ urls.push(url);
1381
+ }
1382
+ });
1383
+
1384
+ // Handle sitemap indexes (sitemaps that link to other sitemaps)
1385
+ const sitemapLinks: string[] = [];
1386
+ $('sitemap > loc').each((_, element) => {
1387
+ const nestedSitemapUrl = $(element).text().trim();
1388
+ if (nestedSitemapUrl) {
1389
+ sitemapLinks.push(nestedSitemapUrl);
1390
+ }
1391
+ });
1392
+
1393
+ // Recursively process nested sitemaps
1394
+ for (const nestedSitemapUrl of sitemapLinks) {
1395
+ logger.debug(`Found nested sitemap: ${nestedSitemapUrl}`);
1396
+ const nestedUrls = await this.parseSitemap(nestedSitemapUrl, logger);
1397
+ urls.push(...nestedUrls);
1398
+ }
1399
+
1400
+ logger.info(`Found ${urls.length} URLs in sitemap ${sitemapUrl}`);
1401
+ return urls;
1402
+ } catch (error) {
1403
+ logger.error(`Error parsing sitemap at ${sitemapUrl}:`, error);
1404
+ return [];
1405
+ }
1406
+ }
1407
+
1367
1408
  private async crawlWebsite(
1368
1409
  baseUrl: string,
1369
1410
  sourceConfig: WebsiteSourceConfig,
@@ -1373,8 +1414,24 @@ class Doc2Vec {
1373
1414
  ): Promise<void> {
1374
1415
  const logger = parentLogger.child('crawler');
1375
1416
  const queue: string[] = [baseUrl];
1417
+
1418
+ // Process sitemap if provided
1419
+ if (sourceConfig.sitemap_url) {
1420
+ logger.section('SITEMAP PROCESSING');
1421
+ const sitemapUrls = await this.parseSitemap(sourceConfig.sitemap_url, logger);
1422
+
1423
+ // Add sitemap URLs to the queue if they're within the website scope
1424
+ for (const url of sitemapUrls) {
1425
+ if (url.startsWith(sourceConfig.url) && !queue.includes(url)) {
1426
+ logger.debug(`Adding URL from sitemap to queue: ${url}`);
1427
+ queue.push(url);
1428
+ }
1429
+ }
1430
+
1431
+ logger.info(`Added ${queue.length - 1} URLs from sitemap to the crawl queue`);
1432
+ }
1376
1433
 
1377
- logger.info(`Starting crawl from ${baseUrl}`);
1434
+ logger.info(`Starting crawl from ${baseUrl} with ${queue.length} URLs in initial queue`);
1378
1435
  let processedCount = 0;
1379
1436
  let skippedCount = 0;
1380
1437
  let skippedSizeCount = 0;
package/logger.ts ADDED
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Enhanced Logger for structured and consistent logging
3
+ * Compatible with CommonJS and ESM environments
4
+ */
5
+
6
+ /**
7
+ * Logger levels with their corresponding numeric values
8
+ */
9
+ enum LogLevel {
10
+ DEBUG = 0,
11
+ INFO = 1,
12
+ WARN = 2,
13
+ ERROR = 3,
14
+ NONE = 100
15
+ }
16
+
17
+ /**
18
+ * Configuration options for the Logger
19
+ */
20
+ interface LoggerConfig {
21
+ level: LogLevel;
22
+ useTimestamp: boolean;
23
+ useColor: boolean;
24
+ logToFile?: string;
25
+ prettyPrint?: boolean;
26
+ }
27
+
28
+ /**
29
+ * Basic color functions that don't rely on external packages
30
+ */
31
+ const colors = {
32
+ gray: (text: string) => `\x1b[90m${text}\x1b[0m`,
33
+ blue: (text: string) => `\x1b[34m${text}\x1b[0m`,
34
+ yellow: (text: string) => `\x1b[33m${text}\x1b[0m`,
35
+ red: (text: string) => `\x1b[31m${text}\x1b[0m`,
36
+ green: (text: string) => `\x1b[32m${text}\x1b[0m`,
37
+ reset: (text: string) => `\x1b[0m${text}\x1b[0m`
38
+ };
39
+
40
+ /**
41
+ * Enhanced Logger class with color-coding, timestamp, and formatting
42
+ */
43
+ class Logger {
44
+ private config: LoggerConfig;
45
+ private moduleName: string;
46
+
47
+ /**
48
+ * Create a new Logger instance
49
+ *
50
+ * @param moduleName Name of the module using this logger
51
+ * @param config Logger configuration options
52
+ */
53
+ constructor(moduleName: string, config?: Partial<LoggerConfig>) {
54
+ this.moduleName = moduleName;
55
+ this.config = {
56
+ level: LogLevel.INFO,
57
+ useTimestamp: true,
58
+ useColor: true,
59
+ prettyPrint: true,
60
+ ...config
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Format a log message with timestamp, level, and module information
66
+ *
67
+ * @param level Log level for this message
68
+ * @param message The message to log
69
+ * @param args Additional arguments to include
70
+ * @returns Formatted log message
71
+ */
72
+ private formatMessage(level: string, message: string, args: any[] = []): string {
73
+ const timestamp = this.config.useTimestamp ?
74
+ `[${new Date().toISOString()}] ` : '';
75
+
76
+ const modulePrefix = this.moduleName ?
77
+ `[${this.moduleName}] ` : '';
78
+
79
+ const levelFormatted = `[${level.padEnd(5)}]`;
80
+
81
+ let formattedMessage = `${timestamp}${levelFormatted} ${modulePrefix}${message}`;
82
+
83
+ if (args.length > 0) {
84
+ const argsString = args.map(arg => {
85
+ if (arg instanceof Error) {
86
+ return `\n--- Error Details ---\nMessage: ${arg.message}\nStack:\n${arg.stack}\n--- End Error ---`;
87
+ }
88
+ else if (this.config.prettyPrint && typeof arg === 'object' && arg !== null) {
89
+ try {
90
+ return JSON.stringify(arg, null, 2);
91
+ } catch (e) {
92
+ return "[Unserializable Object]";
93
+ }
94
+ } else {
95
+ return String(arg);
96
+ }
97
+ }).join('\n');
98
+
99
+ if (this.config.prettyPrint) {
100
+ formattedMessage += `\n${argsString}`;
101
+ } else {
102
+ formattedMessage += ` ${args.map(String).join(' ')}`;
103
+ }
104
+ }
105
+
106
+ return formattedMessage;
107
+ }
108
+
109
+ /**
110
+ * Apply color to a message based on log level
111
+ *
112
+ * @param level Log level
113
+ * @param message Message to color
114
+ * @returns Colored message
115
+ */
116
+ private colorize(level: LogLevel, message: string): string {
117
+ if (!this.config.useColor) return message;
118
+
119
+ switch (level) {
120
+ case LogLevel.DEBUG:
121
+ return colors.gray(message);
122
+ case LogLevel.INFO:
123
+ return colors.blue(message);
124
+ case LogLevel.WARN:
125
+ return colors.yellow(message);
126
+ case LogLevel.ERROR:
127
+ return colors.red(message);
128
+ default:
129
+ return message;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Log a debug message
135
+ *
136
+ * @param message Message to log
137
+ * @param args Additional arguments
138
+ */
139
+ debug(message: string, ...args: any[]): void {
140
+ if (this.config.level <= LogLevel.DEBUG) {
141
+ const formattedMessage = this.formatMessage('DEBUG', message, args);
142
+ console.log(this.colorize(LogLevel.DEBUG, formattedMessage));
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Log an info message
148
+ *
149
+ * @param message Message to log
150
+ * @param args Additional arguments
151
+ */
152
+ info(message: string, ...args: any[]): void {
153
+ if (this.config.level <= LogLevel.INFO) {
154
+ const formattedMessage = this.formatMessage('INFO', message, args);
155
+ console.log(this.colorize(LogLevel.INFO, formattedMessage));
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Log a warning message
161
+ *
162
+ * @param message Message to log
163
+ * @param args Additional arguments
164
+ */
165
+ warn(message: string, ...args: any[]): void {
166
+ if (this.config.level <= LogLevel.WARN) {
167
+ const formattedMessage = this.formatMessage('WARN', message, args);
168
+ console.warn(this.colorize(LogLevel.WARN, formattedMessage));
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Log an error message
174
+ *
175
+ * @param message Message to log
176
+ * @param args Additional arguments
177
+ */
178
+ error(message: string, ...args: any[]): void {
179
+ if (this.config.level <= LogLevel.ERROR) {
180
+ const formattedMessage = this.formatMessage('ERROR', message, args);
181
+ console.error(this.colorize(LogLevel.ERROR, formattedMessage));
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Create a child logger with a more specific module name
187
+ *
188
+ * @param subModule Name of the sub-module
189
+ * @returns New logger instance
190
+ */
191
+ child(subModule: string): Logger {
192
+ return new Logger(`${this.moduleName}:${subModule}`, this.config);
193
+ }
194
+
195
+ /**
196
+ * Format a section header to clearly separate logical parts of execution
197
+ *
198
+ * @param title Section title
199
+ * @returns Logger instance for chaining
200
+ */
201
+ section(title: string): Logger {
202
+ if (this.config.level <= LogLevel.INFO) {
203
+ const separator = '='.repeat(Math.max(80 - title.length - 4, 10));
204
+ const message = `${separator} ${title} ${separator}`;
205
+ console.log(this.colorize(LogLevel.INFO, message));
206
+ }
207
+ return this;
208
+ }
209
+
210
+ /**
211
+ * Create a progress indicator
212
+ *
213
+ * @param title Title of the operation
214
+ * @param total Total number of items to process
215
+ * @returns Object with update and complete methods
216
+ */
217
+ progress(title: string, total: number) {
218
+ let current = 0;
219
+ const startTime = Date.now();
220
+
221
+ const update = (increment = 1, message?: string) => {
222
+ if (this.config.level > LogLevel.INFO) return;
223
+
224
+ current += increment;
225
+ const percentage = Math.min(Math.floor((current / total) * 100), 100);
226
+ const elapsed = (Date.now() - startTime) / 1000;
227
+ let rate = current / elapsed;
228
+
229
+ let timeRemaining = '';
230
+ if (rate > 0 && current < total) {
231
+ const remainingSecs = (total - current) / rate;
232
+ timeRemaining = `, ETA: ${Math.floor(remainingSecs / 60)}m ${Math.floor(remainingSecs % 60)}s`;
233
+ }
234
+
235
+ const progressBar = this.createProgressBar(percentage);
236
+
237
+ const statusMsg = message ? ` - ${message}` : '';
238
+ console.log(this.colorize(
239
+ LogLevel.INFO,
240
+ this.formatMessage('INFO', `${title}: ${progressBar} ${percentage}% (${current}/${total}${timeRemaining})${statusMsg}`)
241
+ ));
242
+ };
243
+
244
+ const complete = (message = 'Completed') => {
245
+ if (this.config.level > LogLevel.INFO) return;
246
+
247
+ const elapsed = (Date.now() - startTime) / 1000;
248
+ const rate = total / elapsed;
249
+
250
+ console.log(this.colorize(
251
+ LogLevel.INFO,
252
+ this.formatMessage('INFO', `${title}: ${this.createProgressBar(100)} 100% (${total}/${total}) - ${message} in ${elapsed.toFixed(2)}s (${rate.toFixed(2)} items/sec)`)
253
+ ));
254
+ };
255
+
256
+ return { update, complete };
257
+ }
258
+
259
+ /**
260
+ * Create a visual progress bar
261
+ *
262
+ * @param percentage Completion percentage
263
+ * @returns Visual progress bar
264
+ */
265
+ private createProgressBar(percentage: number): string {
266
+ const width = 20;
267
+ const completeChars = Math.floor((percentage / 100) * width);
268
+ const incompleteChars = width - completeChars;
269
+
270
+ let bar = '[';
271
+ if (this.config.useColor) {
272
+ bar += colors.green('='.repeat(completeChars));
273
+ bar += ' '.repeat(incompleteChars);
274
+ } else {
275
+ bar += '='.repeat(completeChars);
276
+ bar += ' '.repeat(incompleteChars);
277
+ }
278
+ bar += ']';
279
+
280
+ return bar;
281
+ }
282
+ }
283
+
284
+ // Create a default logger instance
285
+ const defaultLogger = new Logger('app');
286
+
287
+ export { Logger, LogLevel, defaultLogger };
package/mcp/Dockerfile ADDED
@@ -0,0 +1,14 @@
1
+ FROM node:20-slim AS base
2
+
3
+ # Copy project files
4
+ COPY package.json /usr/src/app/
5
+ COPY package-lock.json /usr/src/app/
6
+ COPY tsconfig.json /usr/src/app/
7
+ COPY src/index.ts /usr/src/app/src/
8
+ COPY *.db /data/
9
+ WORKDIR /usr/src/app
10
+
11
+ RUN npm install
12
+ RUN npm run build
13
+
14
+ ENTRYPOINT ["node", "build/index.js"]
package/mcp/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # SQLite Vector Documentation Query MCP Server
2
+
3
+ This is a Model Context Protocol (MCP) server that enables querying documentation stored in SQLite databases with vector embeddings. The server uses OpenAI's embedding API to convert natural language queries into vector embeddings and performs semantic search against documentation stored in SQLite databases.
4
+
5
+ ## Features
6
+
7
+ - Vector-based semantic search for documentation
8
+ - Filters by product name and version
9
+ - Uses OpenAI's embedding API for query embedding generation
10
+ - Fully compatible with the Model Context Protocol
11
+ - Simple Express-based API with Server-Sent Events (SSE) for real-time communication
12
+
13
+ ## Prerequisites
14
+
15
+ - Node.js 20 or higher
16
+ - OpenAI API key
17
+ - Documentation stored in SQLite vector databases (using `sqlite-vec`)
18
+
19
+ ## Environment Variables
20
+
21
+ | Variable | Description | Default |
22
+ |----------|-------------|---------|
23
+ | `OPENAI_API_KEY` | Your OpenAI API key (required) | - |
24
+ | `SQLITE_DB_DIR` | Directory containing SQLite databases | Current directory |
25
+ | `PORT` | Port to run the server on | 3001 |
26
+
27
+ ## Local Setup and Running
28
+
29
+ 1. Install dependencies:
30
+ ```bash
31
+ npm install
32
+ ```
33
+
34
+ 2. Create a `.env` file with required environment variables:
35
+ ```
36
+ OPENAI_API_KEY=your_openai_api_key
37
+ SQLITE_DB_DIR=/path/to/databases
38
+ PORT=3001
39
+ ```
40
+
41
+ 3. Build the TypeScript code:
42
+ ```bash
43
+ npm run build
44
+ ```
45
+
46
+ 4. Start the server:
47
+ ```bash
48
+ npm start
49
+ ```
50
+
51
+ ## Docker Setup
52
+
53
+ ### Building the Docker Image
54
+
55
+ ```bash
56
+ docker build -t sqlite-vec-mcp-server:latest .
57
+ ```
58
+
59
+ This is going to include any `*.db` files in the `/data` directory of the image.
60
+
61
+ ### Running with Docker
62
+
63
+ ```bash
64
+ docker run -p 3001:3001 \
65
+ -e OPENAI_API_KEY=your_openai_api_key \
66
+ sqlite-vec-mcp-server:latest
67
+ ```
68
+
69
+ ### Create a Secret for the OpenAI API Key
70
+
71
+ ```bash
72
+ kubectl create secret generic mcp-secrets \
73
+ --from-literal=OPENAI_API_KEY=your_openai_api_key
74
+ ```
75
+
76
+ ### Create a ConfigMap for Database Configuration
77
+
78
+ ```bash
79
+ kubectl create configmap mcp-config \
80
+ --from-literal=SQLITE_DB_DIR=/data \
81
+ --from-literal=PORT=3001
82
+ ```
83
+
84
+ ### Create a Deployment
85
+
86
+ Create a file named `deployment.yaml`:
87
+
88
+ ```yaml
89
+ apiVersion: apps/v1
90
+ kind: Deployment
91
+ metadata:
92
+ name: mcp-sqlite-vec
93
+ labels:
94
+ app: mcp-sqlite-vec
95
+ spec:
96
+ replicas: 1
97
+ selector:
98
+ matchLabels:
99
+ app: mcp-sqlite-vec
100
+ template:
101
+ metadata:
102
+ labels:
103
+ app: mcp-sqlite-vec
104
+ spec:
105
+ containers:
106
+ - name: mcp-sqlite-vec
107
+ image: sqlite-vec-mcp-server:latest
108
+ imagePullPolicy: Always
109
+ ports:
110
+ - containerPort: 3001
111
+ env:
112
+ - name: OPENAI_API_KEY
113
+ valueFrom:
114
+ secretKeyRef:
115
+ name: mcp-secrets
116
+ key: OPENAI_API_KEY
117
+ - name: SQLITE_DB_DIR
118
+ valueFrom:
119
+ configMapKeyRef:
120
+ name: mcp-config
121
+ key: SQLITE_DB_DIR
122
+ - name: PORT
123
+ valueFrom:
124
+ configMapKeyRef:
125
+ name: mcp-config
126
+ key: PORT
127
+ ```
128
+
129
+ Apply it:
130
+ ```bash
131
+ kubectl apply -f deployment.yaml
132
+ ```
133
+
134
+ ### Create a Service
135
+
136
+ Create a file named `service.yaml`:
137
+
138
+ ```yaml
139
+ apiVersion: v1
140
+ kind: Service
141
+ metadata:
142
+ name: mcp-sqlite-vec
143
+ spec:
144
+ selector:
145
+ app: mcp-sqlite-vec
146
+ ports:
147
+ - port: 3001
148
+ targetPort: 3001
149
+ type: ClusterIP
150
+ ```
151
+
152
+ Apply it:
153
+ ```bash
154
+ kubectl apply -f service.yaml
155
+ ```
156
+
157
+ ## Using the MCP Server
158
+
159
+ The server implements a tool called `query-documentation` that can be used to query documentation.
160
+
161
+ ### Tool Parameters
162
+
163
+ - `queryText` (string, required): The natural language query to search for
164
+ - `productName` (string, required): The name of the product documentation database to search within
165
+ - `version` (string, optional): The specific version of the product documentation
166
+ - `limit` (number, optional, default: 4): Maximum number of results to return