@mhalder/qdrant-mcp-server 3.2.0 → 3.3.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 (132) hide show
  1. package/.dagger/.gitattributes +1 -0
  2. package/.dagger/package.json +6 -0
  3. package/.dagger/src/index.ts +83 -0
  4. package/.dagger/tsconfig.json +13 -0
  5. package/.dagger/yarn.lock +8 -0
  6. package/.github/workflows/ci.yml +17 -27
  7. package/.github/workflows/release.yml +16 -19
  8. package/CHANGELOG.md +13 -0
  9. package/README.md +11 -9
  10. package/build/code/chunker/tree-sitter-chunker.d.ts.map +1 -1
  11. package/build/code/chunker/tree-sitter-chunker.js +15 -3
  12. package/build/code/chunker/tree-sitter-chunker.js.map +1 -1
  13. package/build/code/indexer.d.ts +1 -0
  14. package/build/code/indexer.d.ts.map +1 -1
  15. package/build/code/indexer.js +24 -4
  16. package/build/code/indexer.js.map +1 -1
  17. package/build/embeddings/cohere.d.ts +1 -0
  18. package/build/embeddings/cohere.d.ts.map +1 -1
  19. package/build/embeddings/cohere.js +8 -1
  20. package/build/embeddings/cohere.js.map +1 -1
  21. package/build/embeddings/cohere.test.js +11 -0
  22. package/build/embeddings/cohere.test.js.map +1 -1
  23. package/build/embeddings/factory.d.ts.map +1 -1
  24. package/build/embeddings/factory.js +2 -0
  25. package/build/embeddings/factory.js.map +1 -1
  26. package/build/embeddings/factory.test.js +12 -1
  27. package/build/embeddings/factory.test.js.map +1 -1
  28. package/build/embeddings/ollama.d.ts +1 -0
  29. package/build/embeddings/ollama.d.ts.map +1 -1
  30. package/build/embeddings/ollama.js +8 -1
  31. package/build/embeddings/ollama.js.map +1 -1
  32. package/build/embeddings/ollama.test.js +11 -0
  33. package/build/embeddings/ollama.test.js.map +1 -1
  34. package/build/embeddings/openai.d.ts +1 -0
  35. package/build/embeddings/openai.d.ts.map +1 -1
  36. package/build/embeddings/openai.js +8 -1
  37. package/build/embeddings/openai.js.map +1 -1
  38. package/build/embeddings/openai.test.js +11 -0
  39. package/build/embeddings/openai.test.js.map +1 -1
  40. package/build/embeddings/voyage.d.ts +1 -0
  41. package/build/embeddings/voyage.d.ts.map +1 -1
  42. package/build/embeddings/voyage.js +8 -1
  43. package/build/embeddings/voyage.js.map +1 -1
  44. package/build/embeddings/voyage.test.js +11 -0
  45. package/build/embeddings/voyage.test.js.map +1 -1
  46. package/build/git/indexer.d.ts +1 -0
  47. package/build/git/indexer.d.ts.map +1 -1
  48. package/build/git/indexer.js +16 -3
  49. package/build/git/indexer.js.map +1 -1
  50. package/build/git/indexer.test.js +15 -9
  51. package/build/git/indexer.test.js.map +1 -1
  52. package/build/index.js +35 -26
  53. package/build/index.js.map +1 -1
  54. package/build/index.test.js +105 -91
  55. package/build/index.test.js.map +1 -1
  56. package/build/logger.d.ts +4 -0
  57. package/build/logger.d.ts.map +1 -0
  58. package/build/logger.js +24 -0
  59. package/build/logger.js.map +1 -0
  60. package/build/qdrant/client.d.ts +1 -0
  61. package/build/qdrant/client.d.ts.map +1 -1
  62. package/build/qdrant/client.js +10 -0
  63. package/build/qdrant/client.js.map +1 -1
  64. package/build/qdrant/client.test.js +11 -0
  65. package/build/qdrant/client.test.js.map +1 -1
  66. package/build/tools/code.d.ts.map +1 -1
  67. package/build/tools/code.js +44 -13
  68. package/build/tools/code.js.map +1 -1
  69. package/build/tools/collection.d.ts.map +1 -1
  70. package/build/tools/collection.js +15 -8
  71. package/build/tools/collection.js.map +1 -1
  72. package/build/tools/document.d.ts.map +1 -1
  73. package/build/tools/document.js +9 -4
  74. package/build/tools/document.js.map +1 -1
  75. package/build/tools/federated.d.ts.map +1 -1
  76. package/build/tools/federated.js +9 -4
  77. package/build/tools/federated.js.map +1 -1
  78. package/build/tools/federated.test.js +11 -0
  79. package/build/tools/federated.test.js.map +1 -1
  80. package/build/tools/git-history.d.ts.map +1 -1
  81. package/build/tools/git-history.js +44 -12
  82. package/build/tools/git-history.js.map +1 -1
  83. package/build/tools/logging.d.ts +16 -0
  84. package/build/tools/logging.d.ts.map +1 -0
  85. package/build/tools/logging.js +68 -0
  86. package/build/tools/logging.js.map +1 -0
  87. package/build/tools/logging.test.d.ts +2 -0
  88. package/build/tools/logging.test.d.ts.map +1 -0
  89. package/build/tools/logging.test.js +139 -0
  90. package/build/tools/logging.test.js.map +1 -0
  91. package/build/tools/schemas.d.ts +32 -19
  92. package/build/tools/schemas.d.ts.map +1 -1
  93. package/build/tools/schemas.js +9 -3
  94. package/build/tools/schemas.js.map +1 -1
  95. package/build/tools/search.d.ts.map +1 -1
  96. package/build/tools/search.js +13 -4
  97. package/build/tools/search.js.map +1 -1
  98. package/dagger.json +8 -0
  99. package/mise.toml +2 -0
  100. package/package.json +14 -13
  101. package/src/code/chunker/tree-sitter-chunker.ts +41 -9
  102. package/src/code/indexer.ts +41 -6
  103. package/src/embeddings/cohere.test.ts +12 -0
  104. package/src/embeddings/cohere.ts +10 -2
  105. package/src/embeddings/factory.test.ts +13 -1
  106. package/src/embeddings/factory.ts +3 -0
  107. package/src/embeddings/ollama.test.ts +12 -0
  108. package/src/embeddings/ollama.ts +10 -2
  109. package/src/embeddings/openai.test.ts +12 -0
  110. package/src/embeddings/openai.ts +10 -2
  111. package/src/embeddings/voyage.test.ts +12 -0
  112. package/src/embeddings/voyage.ts +10 -2
  113. package/src/git/indexer.test.ts +22 -16
  114. package/src/git/indexer.ts +30 -4
  115. package/src/index.test.ts +128 -106
  116. package/src/index.ts +59 -38
  117. package/src/logger.ts +33 -0
  118. package/src/qdrant/client.test.ts +12 -0
  119. package/src/qdrant/client.ts +22 -0
  120. package/src/tools/code.ts +107 -62
  121. package/src/tools/collection.ts +39 -22
  122. package/src/tools/document.ts +52 -22
  123. package/src/tools/federated.test.ts +12 -0
  124. package/src/tools/federated.ts +143 -125
  125. package/src/tools/git-history.ts +117 -60
  126. package/src/tools/logging.test.ts +206 -0
  127. package/src/tools/logging.ts +85 -0
  128. package/src/tools/schemas.ts +9 -3
  129. package/src/tools/search.ts +93 -71
  130. package/tests/code/chunker/tree-sitter-chunker.test.ts +13 -1
  131. package/tests/code/indexer.test.ts +12 -0
  132. package/tests/code/integration.test.ts +14 -1
@@ -5,6 +5,7 @@
5
5
  import { createHash } from "node:crypto";
6
6
  import { promises as fs } from "node:fs";
7
7
  import { resolve } from "node:path";
8
+ import logger from "../logger.js";
8
9
  import type { EmbeddingProvider } from "../embeddings/base.js";
9
10
  import { BM25SparseVectorGenerator } from "../embeddings/sparse.js";
10
11
  import type { QdrantManager } from "../qdrant/client.js";
@@ -25,6 +26,8 @@ import type {
25
26
  } from "./types.js";
26
27
 
27
28
  export class GitHistoryIndexer {
29
+ private log = logger.child({ component: "git-indexer" });
30
+
28
31
  constructor(
29
32
  private qdrant: QdrantManager,
30
33
  private embeddings: EmbeddingProvider,
@@ -66,6 +69,11 @@ export class GitHistoryIndexer {
66
69
  const absolutePath = await this.validatePath(path);
67
70
  const collectionName = await this.getCollectionName(absolutePath);
68
71
 
72
+ this.log.info(
73
+ { path: absolutePath, collectionName },
74
+ "Git indexing started",
75
+ );
76
+
69
77
  try {
70
78
  // 1. Validate repository
71
79
  const extractor = new GitExtractor(absolutePath, this.config);
@@ -119,6 +127,7 @@ export class GitHistoryIndexer {
119
127
  });
120
128
 
121
129
  stats.commitsScanned = commits.length;
130
+ this.log.info({ commitsExtracted: commits.length }, "Commits extracted");
122
131
 
123
132
  if (commits.length === 0) {
124
133
  await this.storeIndexingMarker(collectionName, true);
@@ -175,6 +184,10 @@ export class GitHistoryIndexer {
175
184
 
176
185
  // 5. Generate embeddings and store in batches
177
186
  const batchSize = this.config.batchSize;
187
+ this.log.debug(
188
+ { totalChunks: allChunks.length, batchSize },
189
+ "Starting embedding generation",
190
+ );
178
191
  for (let i = 0; i < allChunks.length; i += batchSize) {
179
192
  const batch = allChunks.slice(i, i + batchSize);
180
193
 
@@ -232,7 +245,9 @@ export class GitHistoryIndexer {
232
245
  const sparseGenerator = new BM25SparseVectorGenerator();
233
246
  const hybridPoints = points.map((point, idx) => ({
234
247
  ...point,
235
- sparseVector: sparseGenerator.generate(batch[idx].chunk.content),
248
+ sparseVector: sparseGenerator.generate(
249
+ batch[idx].chunk.content,
250
+ ),
236
251
  }));
237
252
  await this.qdrant.addPointsWithSparse(
238
253
  collectionName,
@@ -245,7 +260,8 @@ export class GitHistoryIndexer {
245
260
  success = true;
246
261
  break;
247
262
  } catch (error) {
248
- lastError = error instanceof Error ? error : new Error(String(error));
263
+ lastError =
264
+ error instanceof Error ? error : new Error(String(error));
249
265
  if (attempt < this.config.batchRetryAttempts) {
250
266
  // Exponential backoff: 1s, 2s, 4s...
251
267
  const delay = Math.pow(2, attempt - 1) * 1000;
@@ -270,7 +286,7 @@ export class GitHistoryIndexer {
270
286
  } catch (error) {
271
287
  const errorMessage =
272
288
  error instanceof Error ? error.message : String(error);
273
- console.error("Failed to save snapshot:", errorMessage);
289
+ this.log.error({ err: error }, "Failed to save snapshot");
274
290
  stats.errors?.push(`Snapshot save failed: ${errorMessage}`);
275
291
  }
276
292
 
@@ -278,6 +294,14 @@ export class GitHistoryIndexer {
278
294
  await this.storeIndexingMarker(collectionName, true);
279
295
 
280
296
  stats.durationMs = Date.now() - startTime;
297
+ this.log.info(
298
+ {
299
+ commitsIndexed: stats.commitsIndexed,
300
+ chunksCreated: stats.chunksCreated,
301
+ durationMs: stats.durationMs,
302
+ },
303
+ "Git indexing complete",
304
+ );
281
305
  return stats;
282
306
  } catch (error) {
283
307
  const errorMessage =
@@ -509,6 +533,7 @@ export class GitHistoryIndexer {
509
533
  });
510
534
 
511
535
  stats.newCommits = newCommits.length;
536
+ this.log.info({ newCommits: newCommits.length }, "New commits found");
512
537
 
513
538
  if (newCommits.length === 0) {
514
539
  stats.durationMs = Date.now() - startTime;
@@ -612,6 +637,7 @@ export class GitHistoryIndexer {
612
637
  * Clear all indexed data for a repository
613
638
  */
614
639
  async clearIndex(path: string): Promise<void> {
640
+ this.log.info({ path }, "Clearing git index");
615
641
  const absolutePath = await this.validatePath(path);
616
642
  const collectionName = await this.getCollectionName(absolutePath);
617
643
  const exists = await this.qdrant.collectionExists(collectionName);
@@ -670,7 +696,7 @@ export class GitHistoryIndexer {
670
696
  ]);
671
697
  }
672
698
  } catch (error) {
673
- console.error("Failed to store indexing marker:", error);
699
+ this.log.error({ err: error }, "Failed to store indexing marker");
674
700
  }
675
701
  }
676
702
 
package/src/index.test.ts CHANGED
@@ -1,190 +1,212 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
-
3
- vi.mock('./qdrant/client.js');
4
- vi.mock('./embeddings/openai.js');
5
-
6
- describe('MCP Server Tool Schemas', () => {
7
- describe('CreateCollectionSchema', () => {
8
- it('should validate correct collection creation input', async () => {
9
- const { z } = await import('zod');
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+
3
+ vi.mock("./qdrant/client.js");
4
+ vi.mock("./embeddings/openai.js");
5
+ vi.mock("./logger.js", () => ({
6
+ default: {
7
+ info: vi.fn(),
8
+ warn: vi.fn(),
9
+ error: vi.fn(),
10
+ debug: vi.fn(),
11
+ fatal: vi.fn(),
12
+ trace: vi.fn(),
13
+ child: vi.fn().mockReturnThis(),
14
+ },
15
+ }));
16
+
17
+ describe("MCP Server Tool Schemas", () => {
18
+ describe("CreateCollectionSchema", () => {
19
+ it("should validate correct collection creation input", async () => {
20
+ const { z } = await import("zod");
10
21
 
11
22
  const CreateCollectionSchema = z.object({
12
23
  name: z.string(),
13
- distance: z.enum(['Cosine', 'Euclid', 'Dot']).optional(),
24
+ distance: z.enum(["Cosine", "Euclid", "Dot"]).optional(),
14
25
  });
15
26
 
16
- const validInput = { name: 'test-collection' };
27
+ const validInput = { name: "test-collection" };
17
28
  expect(() => CreateCollectionSchema.parse(validInput)).not.toThrow();
18
29
 
19
- const validInputWithDistance = { name: 'test-collection', distance: 'Cosine' as const };
20
- expect(() => CreateCollectionSchema.parse(validInputWithDistance)).not.toThrow();
30
+ const validInputWithDistance = {
31
+ name: "test-collection",
32
+ distance: "Cosine" as const,
33
+ };
34
+ expect(() =>
35
+ CreateCollectionSchema.parse(validInputWithDistance),
36
+ ).not.toThrow();
21
37
  });
22
38
 
23
- it('should reject invalid distance metric', async () => {
24
- const { z } = await import('zod');
39
+ it("should reject invalid distance metric", async () => {
40
+ const { z } = await import("zod");
25
41
 
26
42
  const CreateCollectionSchema = z.object({
27
43
  name: z.string(),
28
- distance: z.enum(['Cosine', 'Euclid', 'Dot']).optional(),
44
+ distance: z.enum(["Cosine", "Euclid", "Dot"]).optional(),
29
45
  });
30
46
 
31
- const invalidInput = { name: 'test', distance: 'Invalid' };
47
+ const invalidInput = { name: "test", distance: "Invalid" };
32
48
  expect(() => CreateCollectionSchema.parse(invalidInput)).toThrow();
33
49
  });
34
50
 
35
- it('should require name field', async () => {
36
- const { z } = await import('zod');
51
+ it("should require name field", async () => {
52
+ const { z } = await import("zod");
37
53
 
38
54
  const CreateCollectionSchema = z.object({
39
55
  name: z.string(),
40
- distance: z.enum(['Cosine', 'Euclid', 'Dot']).optional(),
56
+ distance: z.enum(["Cosine", "Euclid", "Dot"]).optional(),
41
57
  });
42
58
 
43
- const invalidInput = { distance: 'Cosine' };
59
+ const invalidInput = { distance: "Cosine" };
44
60
  expect(() => CreateCollectionSchema.parse(invalidInput)).toThrow();
45
61
  });
46
62
  });
47
63
 
48
- describe('AddDocumentsSchema', () => {
49
- it('should validate correct document addition input', async () => {
50
- const { z } = await import('zod');
64
+ describe("AddDocumentsSchema", () => {
65
+ it("should validate correct document addition input", async () => {
66
+ const { z } = await import("zod");
51
67
 
52
68
  const AddDocumentsSchema = z.object({
53
69
  collection: z.string(),
54
- documents: z.array(z.object({
55
- id: z.union([z.string(), z.number()]),
56
- text: z.string(),
57
- metadata: z.record(z.any()).optional(),
58
- })),
70
+ documents: z.array(
71
+ z.object({
72
+ id: z.union([z.string(), z.number()]),
73
+ text: z.string(),
74
+ metadata: z.record(z.string(), z.any()).optional(),
75
+ }),
76
+ ),
59
77
  });
60
78
 
61
79
  const validInput = {
62
- collection: 'test-collection',
80
+ collection: "test-collection",
63
81
  documents: [
64
- { id: 1, text: 'test document' },
65
- { id: 'doc-2', text: 'another document', metadata: { type: 'test' } },
82
+ { id: 1, text: "test document" },
83
+ { id: "doc-2", text: "another document", metadata: { type: "test" } },
66
84
  ],
67
85
  };
68
86
 
69
87
  expect(() => AddDocumentsSchema.parse(validInput)).not.toThrow();
70
88
  });
71
89
 
72
- it('should accept both string and number IDs', async () => {
73
- const { z } = await import('zod');
90
+ it("should accept both string and number IDs", async () => {
91
+ const { z } = await import("zod");
74
92
 
75
93
  const AddDocumentsSchema = z.object({
76
94
  collection: z.string(),
77
- documents: z.array(z.object({
78
- id: z.union([z.string(), z.number()]),
79
- text: z.string(),
80
- metadata: z.record(z.any()).optional(),
81
- })),
95
+ documents: z.array(
96
+ z.object({
97
+ id: z.union([z.string(), z.number()]),
98
+ text: z.string(),
99
+ metadata: z.record(z.string(), z.any()).optional(),
100
+ }),
101
+ ),
82
102
  });
83
103
 
84
104
  const stringIdInput = {
85
- collection: 'test',
86
- documents: [{ id: 'string-id', text: 'test' }],
105
+ collection: "test",
106
+ documents: [{ id: "string-id", text: "test" }],
87
107
  };
88
108
  expect(() => AddDocumentsSchema.parse(stringIdInput)).not.toThrow();
89
109
 
90
110
  const numberIdInput = {
91
- collection: 'test',
92
- documents: [{ id: 123, text: 'test' }],
111
+ collection: "test",
112
+ documents: [{ id: 123, text: "test" }],
93
113
  };
94
114
  expect(() => AddDocumentsSchema.parse(numberIdInput)).not.toThrow();
95
115
  });
96
116
 
97
- it('should require text field in documents', async () => {
98
- const { z } = await import('zod');
117
+ it("should require text field in documents", async () => {
118
+ const { z } = await import("zod");
99
119
 
100
120
  const AddDocumentsSchema = z.object({
101
121
  collection: z.string(),
102
- documents: z.array(z.object({
103
- id: z.union([z.string(), z.number()]),
104
- text: z.string(),
105
- metadata: z.record(z.any()).optional(),
106
- })),
122
+ documents: z.array(
123
+ z.object({
124
+ id: z.union([z.string(), z.number()]),
125
+ text: z.string(),
126
+ metadata: z.record(z.string(), z.any()).optional(),
127
+ }),
128
+ ),
107
129
  });
108
130
 
109
131
  const invalidInput = {
110
- collection: 'test',
132
+ collection: "test",
111
133
  documents: [{ id: 1, metadata: {} }],
112
134
  };
113
135
  expect(() => AddDocumentsSchema.parse(invalidInput)).toThrow();
114
136
  });
115
137
  });
116
138
 
117
- describe('SemanticSearchSchema', () => {
118
- it('should validate correct search input', async () => {
119
- const { z } = await import('zod');
139
+ describe("SemanticSearchSchema", () => {
140
+ it("should validate correct search input", async () => {
141
+ const { z } = await import("zod");
120
142
 
121
143
  const SemanticSearchSchema = z.object({
122
144
  collection: z.string(),
123
145
  query: z.string(),
124
146
  limit: z.number().optional(),
125
- filter: z.record(z.any()).optional(),
147
+ filter: z.record(z.string(), z.any()).optional(),
126
148
  });
127
149
 
128
150
  const validInput = {
129
- collection: 'test-collection',
130
- query: 'search query',
151
+ collection: "test-collection",
152
+ query: "search query",
131
153
  limit: 10,
132
- filter: { category: 'test' },
154
+ filter: { category: "test" },
133
155
  };
134
156
 
135
157
  expect(() => SemanticSearchSchema.parse(validInput)).not.toThrow();
136
158
  });
137
159
 
138
- it('should work with minimal input', async () => {
139
- const { z } = await import('zod');
160
+ it("should work with minimal input", async () => {
161
+ const { z } = await import("zod");
140
162
 
141
163
  const SemanticSearchSchema = z.object({
142
164
  collection: z.string(),
143
165
  query: z.string(),
144
166
  limit: z.number().optional(),
145
- filter: z.record(z.any()).optional(),
167
+ filter: z.record(z.string(), z.any()).optional(),
146
168
  });
147
169
 
148
170
  const minimalInput = {
149
- collection: 'test',
150
- query: 'search',
171
+ collection: "test",
172
+ query: "search",
151
173
  };
152
174
 
153
175
  expect(() => SemanticSearchSchema.parse(minimalInput)).not.toThrow();
154
176
  });
155
177
 
156
- it('should require collection and query', async () => {
157
- const { z } = await import('zod');
178
+ it("should require collection and query", async () => {
179
+ const { z } = await import("zod");
158
180
 
159
181
  const SemanticSearchSchema = z.object({
160
182
  collection: z.string(),
161
183
  query: z.string(),
162
184
  limit: z.number().optional(),
163
- filter: z.record(z.any()).optional(),
185
+ filter: z.record(z.string(), z.any()).optional(),
164
186
  });
165
187
 
166
- const missingQuery = { collection: 'test', limit: 5 };
188
+ const missingQuery = { collection: "test", limit: 5 };
167
189
  expect(() => SemanticSearchSchema.parse(missingQuery)).toThrow();
168
190
 
169
- const missingCollection = { query: 'test', limit: 5 };
191
+ const missingCollection = { query: "test", limit: 5 };
170
192
  expect(() => SemanticSearchSchema.parse(missingCollection)).toThrow();
171
193
  });
172
194
  });
173
195
 
174
- describe('DeleteCollectionSchema', () => {
175
- it('should validate correct delete input', async () => {
176
- const { z } = await import('zod');
196
+ describe("DeleteCollectionSchema", () => {
197
+ it("should validate correct delete input", async () => {
198
+ const { z } = await import("zod");
177
199
 
178
200
  const DeleteCollectionSchema = z.object({
179
201
  name: z.string(),
180
202
  });
181
203
 
182
- const validInput = { name: 'test-collection' };
204
+ const validInput = { name: "test-collection" };
183
205
  expect(() => DeleteCollectionSchema.parse(validInput)).not.toThrow();
184
206
  });
185
207
 
186
- it('should require name field', async () => {
187
- const { z } = await import('zod');
208
+ it("should require name field", async () => {
209
+ const { z } = await import("zod");
188
210
 
189
211
  const DeleteCollectionSchema = z.object({
190
212
  name: z.string(),
@@ -194,22 +216,22 @@ describe('MCP Server Tool Schemas', () => {
194
216
  });
195
217
  });
196
218
 
197
- describe('GetCollectionInfoSchema', () => {
198
- it('should validate correct input', async () => {
199
- const { z } = await import('zod');
219
+ describe("GetCollectionInfoSchema", () => {
220
+ it("should validate correct input", async () => {
221
+ const { z } = await import("zod");
200
222
 
201
223
  const GetCollectionInfoSchema = z.object({
202
224
  name: z.string(),
203
225
  });
204
226
 
205
- const validInput = { name: 'test-collection' };
227
+ const validInput = { name: "test-collection" };
206
228
  expect(() => GetCollectionInfoSchema.parse(validInput)).not.toThrow();
207
229
  });
208
230
  });
209
231
 
210
- describe('DeleteDocumentsSchema', () => {
211
- it('should validate correct delete documents input', async () => {
212
- const { z } = await import('zod');
232
+ describe("DeleteDocumentsSchema", () => {
233
+ it("should validate correct delete documents input", async () => {
234
+ const { z } = await import("zod");
213
235
 
214
236
  const DeleteDocumentsSchema = z.object({
215
237
  collection: z.string(),
@@ -217,40 +239,40 @@ describe('MCP Server Tool Schemas', () => {
217
239
  });
218
240
 
219
241
  const validInput = {
220
- collection: 'test-collection',
221
- ids: [1, 'doc-2', 3],
242
+ collection: "test-collection",
243
+ ids: [1, "doc-2", 3],
222
244
  };
223
245
 
224
246
  expect(() => DeleteDocumentsSchema.parse(validInput)).not.toThrow();
225
247
  });
226
248
 
227
- it('should accept string and number IDs', async () => {
228
- const { z } = await import('zod');
249
+ it("should accept string and number IDs", async () => {
250
+ const { z } = await import("zod");
229
251
 
230
252
  const DeleteDocumentsSchema = z.object({
231
253
  collection: z.string(),
232
254
  ids: z.array(z.union([z.string(), z.number()])),
233
255
  });
234
256
 
235
- const stringIds = { collection: 'test', ids: ['a', 'b', 'c'] };
257
+ const stringIds = { collection: "test", ids: ["a", "b", "c"] };
236
258
  expect(() => DeleteDocumentsSchema.parse(stringIds)).not.toThrow();
237
259
 
238
- const numberIds = { collection: 'test', ids: [1, 2, 3] };
260
+ const numberIds = { collection: "test", ids: [1, 2, 3] };
239
261
  expect(() => DeleteDocumentsSchema.parse(numberIds)).not.toThrow();
240
262
 
241
- const mixedIds = { collection: 'test', ids: [1, 'b', 3] };
263
+ const mixedIds = { collection: "test", ids: [1, "b", 3] };
242
264
  expect(() => DeleteDocumentsSchema.parse(mixedIds)).not.toThrow();
243
265
  });
244
266
 
245
- it('should require both collection and ids', async () => {
246
- const { z } = await import('zod');
267
+ it("should require both collection and ids", async () => {
268
+ const { z } = await import("zod");
247
269
 
248
270
  const DeleteDocumentsSchema = z.object({
249
271
  collection: z.string(),
250
272
  ids: z.array(z.union([z.string(), z.number()])),
251
273
  });
252
274
 
253
- const missingIds = { collection: 'test' };
275
+ const missingIds = { collection: "test" };
254
276
  expect(() => DeleteDocumentsSchema.parse(missingIds)).toThrow();
255
277
 
256
278
  const missingCollection = { ids: [1, 2, 3] };
@@ -259,25 +281,25 @@ describe('MCP Server Tool Schemas', () => {
259
281
  });
260
282
  });
261
283
 
262
- describe('MCP Server Resource URIs', () => {
263
- it('should match collections URI pattern', () => {
264
- const collectionsUri = 'qdrant://collections';
284
+ describe("MCP Server Resource URIs", () => {
285
+ it("should match collections URI pattern", () => {
286
+ const collectionsUri = "qdrant://collections";
265
287
  expect(collectionsUri).toMatch(/^qdrant:\/\/collections$/);
266
288
  });
267
289
 
268
- it('should match collection detail URI pattern', () => {
269
- const collectionUri = 'qdrant://collection/my-collection';
290
+ it("should match collection detail URI pattern", () => {
291
+ const collectionUri = "qdrant://collection/my-collection";
270
292
  const match = collectionUri.match(/^qdrant:\/\/collection\/(.+)$/);
271
293
 
272
294
  expect(match).not.toBeNull();
273
- expect(match![1]).toBe('my-collection');
295
+ expect(match![1]).toBe("my-collection");
274
296
  });
275
297
 
276
- it('should extract collection name from URI', () => {
298
+ it("should extract collection name from URI", () => {
277
299
  const testCases = [
278
- { uri: 'qdrant://collection/test', expected: 'test' },
279
- { uri: 'qdrant://collection/my-docs', expected: 'my-docs' },
280
- { uri: 'qdrant://collection/collection-123', expected: 'collection-123' },
300
+ { uri: "qdrant://collection/test", expected: "test" },
301
+ { uri: "qdrant://collection/my-docs", expected: "my-docs" },
302
+ { uri: "qdrant://collection/collection-123", expected: "collection-123" },
281
303
  ];
282
304
 
283
305
  testCases.forEach(({ uri, expected }) => {
@@ -286,12 +308,12 @@ describe('MCP Server Resource URIs', () => {
286
308
  });
287
309
  });
288
310
 
289
- it('should not match invalid URIs', () => {
311
+ it("should not match invalid URIs", () => {
290
312
  const invalidUris = [
291
- 'qdrant://invalid',
292
- 'http://collections',
293
- 'qdrant://collection/',
294
- 'qdrant:collections',
313
+ "qdrant://invalid",
314
+ "http://collections",
315
+ "qdrant://collection/",
316
+ "qdrant:collections",
295
317
  ];
296
318
 
297
319
  invalidUris.forEach((uri) => {