midnight-mcp 0.2.16 → 0.2.17

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/dist/bin.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  startHttpServer,
4
4
  startServer
5
- } from "./chunk-TJ7QA5XT.js";
5
+ } from "./chunk-GDUMSS3E.js";
6
6
  import {
7
7
  setOutputFormat
8
- } from "./chunk-P3MTQHP6.js";
8
+ } from "./chunk-S33OTE35.js";
9
9
 
10
10
  // src/bin.ts
11
11
  import { config } from "dotenv";
@@ -13,7 +13,7 @@ import { resolve } from "path";
13
13
  import yargs from "yargs";
14
14
  import { hideBin } from "yargs/helpers";
15
15
  config({ path: resolve(process.cwd(), ".env") });
16
- var CURRENT_VERSION = "0.2.16";
16
+ var CURRENT_VERSION = "0.2.17";
17
17
  process.on("uncaughtException", (error) => {
18
18
  console.error("Uncaught exception:", error);
19
19
  process.exit(1);
@@ -25,7 +25,7 @@ import {
25
25
  validateNumber,
26
26
  validateQuery,
27
27
  vectorStore
28
- } from "./chunk-P3MTQHP6.js";
28
+ } from "./chunk-S33OTE35.js";
29
29
 
30
30
  // src/tools/search/schemas.ts
31
31
  import { z } from "zod";
@@ -171,38 +171,53 @@ function finalizeResponse(response, cacheKey, warnings) {
171
171
  });
172
172
  return finalResponse;
173
173
  }
174
- async function searchCompact(input) {
175
- const validation = validateSearchInput(input.query, input.limit);
174
+ async function performSearch(query, limit, config) {
175
+ const validation = validateSearchInput(query, limit);
176
176
  if (!validation.success) {
177
177
  return validation.error;
178
178
  }
179
- const { sanitizedQuery, limit, warnings } = validation.context;
180
- logger.debug("Searching Compact code", {
179
+ const { sanitizedQuery, limit: validatedLimit, warnings } = validation.context;
180
+ logger.debug(`Searching ${config.searchType}`, {
181
181
  query: sanitizedQuery,
182
182
  mode: isHostedMode() ? "hosted" : "local"
183
183
  });
184
184
  const cacheKey = createCacheKey(
185
- "compact",
185
+ config.searchType,
186
186
  sanitizedQuery,
187
- limit,
188
- input.filter?.repository
187
+ validatedLimit,
188
+ ...config.cacheKeyExtra
189
189
  );
190
190
  const cached = checkSearchCache(cacheKey);
191
191
  if (cached) return cached;
192
192
  const hostedResult = await tryHostedSearch(
193
- "compact",
194
- () => searchCompactHosted(sanitizedQuery, limit),
193
+ config.searchType,
194
+ () => config.hostedSearchFn(sanitizedQuery, validatedLimit),
195
195
  cacheKey,
196
196
  warnings
197
197
  );
198
- if (hostedResult) return hostedResult.result;
199
- const filter = {
200
- language: "compact",
201
- ...input.filter
202
- };
203
- const results = await vectorStore.search(sanitizedQuery, limit, filter);
198
+ if (hostedResult) {
199
+ return { ...hostedResult.result, ...config.hostedResultExtra };
200
+ }
201
+ const filter = config.buildFilter();
202
+ let results = await vectorStore.search(sanitizedQuery, validatedLimit, filter);
203
+ if (config.postFilter) {
204
+ results = config.postFilter(results);
205
+ }
204
206
  const response = {
205
- results: results.map((r) => ({
207
+ results: results.map(config.transformResult),
208
+ totalResults: results.length,
209
+ query: sanitizedQuery,
210
+ ...config.extraFields
211
+ };
212
+ return finalizeResponse(response, cacheKey, warnings);
213
+ }
214
+ async function searchCompact(input) {
215
+ return performSearch(input.query, input.limit, {
216
+ searchType: "compact",
217
+ cacheKeyExtra: [input.filter?.repository],
218
+ hostedSearchFn: (query, limit) => searchCompactHosted(query, limit),
219
+ buildFilter: () => ({ language: "compact", ...input.filter }),
220
+ transformResult: (r) => ({
206
221
  code: r.content,
207
222
  relevanceScore: r.score,
208
223
  source: {
@@ -212,49 +227,19 @@ async function searchCompact(input) {
212
227
  },
213
228
  codeType: r.metadata.codeType,
214
229
  name: r.metadata.codeName
215
- })),
216
- totalResults: results.length,
217
- query: sanitizedQuery
218
- };
219
- return finalizeResponse(response, cacheKey, warnings);
230
+ })
231
+ });
220
232
  }
221
233
  async function searchTypeScript(input) {
222
- const validation = validateSearchInput(input.query, input.limit);
223
- if (!validation.success) {
224
- return validation.error;
225
- }
226
- const { sanitizedQuery, limit, warnings } = validation.context;
227
- logger.debug("Searching TypeScript code", {
228
- query: sanitizedQuery,
229
- mode: isHostedMode() ? "hosted" : "local"
230
- });
231
- const cacheKey = createCacheKey(
232
- "typescript",
233
- sanitizedQuery,
234
- limit,
235
- input.includeTypes
236
- );
237
- const cached = checkSearchCache(cacheKey);
238
- if (cached) return cached;
239
- const hostedResult = await tryHostedSearch(
240
- "typescript",
241
- () => searchTypeScriptHosted(sanitizedQuery, limit, input.includeTypes),
242
- cacheKey,
243
- warnings
244
- );
245
- if (hostedResult) return hostedResult.result;
246
- const filter = {
247
- language: "typescript"
248
- };
249
- const results = await vectorStore.search(sanitizedQuery, limit, filter);
250
- let filteredResults = results;
251
- if (!input.includeTypes) {
252
- filteredResults = results.filter(
234
+ return performSearch(input.query, input.limit, {
235
+ searchType: "typescript",
236
+ cacheKeyExtra: [input.includeTypes],
237
+ hostedSearchFn: (query, limit) => searchTypeScriptHosted(query, limit, input.includeTypes),
238
+ buildFilter: () => ({ language: "typescript" }),
239
+ postFilter: (results) => input.includeTypes ? results : results.filter(
253
240
  (r) => r.metadata.codeType !== "type" && r.metadata.codeType !== "interface"
254
- );
255
- }
256
- const response = {
257
- results: filteredResults.map((r) => ({
241
+ ),
242
+ transformResult: (r) => ({
258
243
  code: r.content,
259
244
  relevanceScore: r.score,
260
245
  source: {
@@ -265,49 +250,23 @@ async function searchTypeScript(input) {
265
250
  codeType: r.metadata.codeType,
266
251
  name: r.metadata.codeName,
267
252
  isExported: r.metadata.isPublic
268
- })),
269
- totalResults: filteredResults.length,
270
- query: sanitizedQuery
271
- };
272
- return finalizeResponse(response, cacheKey, warnings);
253
+ })
254
+ });
273
255
  }
274
256
  async function searchDocs(input) {
275
- const validation = validateSearchInput(input.query, input.limit);
276
- if (!validation.success) {
277
- return validation.error;
278
- }
279
- const { sanitizedQuery, limit, warnings } = validation.context;
280
- logger.debug("Searching documentation", {
281
- query: sanitizedQuery,
282
- mode: isHostedMode() ? "hosted" : "local"
283
- });
284
- const cacheKey = createCacheKey(
285
- "docs",
286
- sanitizedQuery,
287
- limit,
288
- input.category
289
- );
290
- const cached = checkSearchCache(cacheKey);
291
- if (cached) return cached;
292
257
  const freshnessHint = "For guaranteed freshness, use midnight-fetch-docs with the path from these results (e.g., /develop/faq)";
293
- const hostedResult = await tryHostedSearch(
294
- "docs",
295
- () => searchDocsHosted(sanitizedQuery, limit, input.category),
296
- cacheKey,
297
- warnings
298
- );
299
- if (hostedResult) {
300
- return { ...hostedResult.result, hint: freshnessHint };
301
- }
302
- const filter = {
303
- language: "markdown"
304
- };
305
- if (input.category !== "all") {
306
- filter.repository = "midnightntwrk/midnight-docs";
307
- }
308
- const results = await vectorStore.search(sanitizedQuery, limit, filter);
309
- const response = {
310
- results: results.map((r) => ({
258
+ return performSearch(input.query, input.limit, {
259
+ searchType: "docs",
260
+ cacheKeyExtra: [input.category],
261
+ hostedSearchFn: (query, limit) => searchDocsHosted(query, limit, input.category),
262
+ buildFilter: () => {
263
+ const filter = { language: "markdown" };
264
+ if (input.category !== "all") {
265
+ filter.repository = "midnightntwrk/midnight-docs";
266
+ }
267
+ return filter;
268
+ },
269
+ transformResult: (r) => ({
311
270
  content: r.content,
312
271
  relevanceScore: r.score,
313
272
  source: {
@@ -315,13 +274,13 @@ async function searchDocs(input) {
315
274
  filePath: r.metadata.filePath,
316
275
  section: r.metadata.codeName
317
276
  }
318
- })),
319
- totalResults: results.length,
320
- query: sanitizedQuery,
321
- category: input.category,
322
- hint: "For guaranteed freshness, use midnight-fetch-docs with the path from these results (e.g., /develop/faq)"
323
- };
324
- return finalizeResponse(response, cacheKey, warnings);
277
+ }),
278
+ extraFields: {
279
+ category: input.category,
280
+ hint: freshnessHint
281
+ },
282
+ hostedResultExtra: { hint: freshnessHint }
283
+ });
325
284
  }
326
285
  var DOCS_BASE_URL = "https://docs.midnight.network";
327
286
  var FETCH_TIMEOUT = 15e3;
@@ -354,7 +313,7 @@ function extractContentFromHtml(html, extractSection) {
354
313
  const headingRegex = /<h([1-6])[^>]*class="[^"]*anchor[^"]*"[^>]*id="([^"]*)"[^>]*>([^<]*(?:<[^/][^>]*>[^<]*<\/[^>]+>)*[^<]*)/gi;
355
314
  let headingMatch;
356
315
  while ((headingMatch = headingRegex.exec(articleHtml)) !== null) {
357
- const text = headingMatch[3].replace(/<[^>]+>/g, "").replace(/\u200B/g, "").replace(/​/g, "").trim();
316
+ const text = headingMatch[3].replace(/<[^>]+>/g, "").replace(/\u200B/g, "").trim();
358
317
  if (text) {
359
318
  headings.push({
360
319
  level: parseInt(headingMatch[1]),
@@ -406,7 +365,24 @@ async function fetchDocs(input) {
406
365
  suggestion: `Use a clean path like '/develop/faq' or '/getting-started/installation'`
407
366
  };
408
367
  }
409
- const url = `${DOCS_BASE_URL}${normalizedPath}`;
368
+ let url;
369
+ try {
370
+ const constructed = new URL(normalizedPath, DOCS_BASE_URL);
371
+ if (constructed.origin !== new URL(DOCS_BASE_URL).origin) {
372
+ return {
373
+ error: "Invalid path",
374
+ details: ["Path resulted in a URL outside the documentation domain"],
375
+ suggestion: `Use a clean path like '/develop/faq' or '/getting-started/installation'`
376
+ };
377
+ }
378
+ url = constructed.href;
379
+ } catch {
380
+ return {
381
+ error: "Invalid path",
382
+ details: ["Could not construct a valid URL from the path"],
383
+ suggestion: `Use a clean path like '/develop/faq' or '/getting-started/installation'`
384
+ };
385
+ }
410
386
  logger.debug("Fetching live documentation", { url, extractSection });
411
387
  try {
412
388
  const controller = new AbortController();
@@ -800,7 +776,19 @@ async function compileContract(code, options = {}) {
800
776
  serviceAvailable: response.status < 500
801
777
  };
802
778
  }
803
- const result = await response.json();
779
+ const rawResult = await response.json();
780
+ if (typeof rawResult !== "object" || rawResult === null || !("success" in rawResult)) {
781
+ logger.error("Compiler API returned unexpected response format", {
782
+ response: JSON.stringify(rawResult).slice(0, 200)
783
+ });
784
+ return {
785
+ success: false,
786
+ message: "Compiler service returned an unexpected response format",
787
+ error: "INVALID_RESPONSE",
788
+ serviceAvailable: true
789
+ };
790
+ }
791
+ const result = rawResult;
804
792
  if (result.success) {
805
793
  const outputInfo = typeof result.output === "object" && result.output !== null ? result.output : {};
806
794
  logger.info("Compilation successful", {
@@ -1710,10 +1698,7 @@ var REPO_ALIASES = {
1710
1698
  lucentlabs: { owner: "statera-protocol", repo: "statera-protocol-midnight" },
1711
1699
  stablecoin: { owner: "statera-protocol", repo: "statera-protocol-midnight" },
1712
1700
  "midnight-bank": { owner: "nel349", repo: "midnight-bank" },
1713
- bank: { owner: "nel349", repo: "midnight-bank" },
1714
- zkbadge: { owner: "Imdavyking", repo: "zkbadge" },
1715
- badge: { owner: "Imdavyking", repo: "zkbadge" },
1716
- davyking: { owner: "Imdavyking", repo: "zkbadge" }
1701
+ bank: { owner: "nel349", repo: "midnight-bank" }
1717
1702
  };
1718
1703
  var EXAMPLES = [
1719
1704
  {
@@ -1837,21 +1822,6 @@ var EXAMPLES = [
1837
1822
  "Private transfers"
1838
1823
  ]
1839
1824
  },
1840
- {
1841
- name: "zkBadge (Davyking)",
1842
- repository: "Imdavyking/zkbadge",
1843
- description: "3rd place Mini DApp winner. Privacy-preserving identity and access control. Issue verifiable credentials (e.g., age proof) without revealing personal data. Only 'verified' status stored on-chain.",
1844
- category: "identity",
1845
- complexity: "intermediate",
1846
- mainFile: "contract/src/zkbadge.compact",
1847
- features: [
1848
- "ZK credentials",
1849
- "Off-chain verification",
1850
- "On-chain badges",
1851
- "Access control",
1852
- "Reputation system"
1853
- ]
1854
- },
1855
1825
  // Core Partner - PaimaStudios (Gaming Infrastructure)
1856
1826
  {
1857
1827
  name: "Midnight Game 2 (PaimaStudios)",
@@ -4176,7 +4146,7 @@ async function getDocumentation(uri) {
4176
4146
  if (file) {
4177
4147
  return file.content;
4178
4148
  }
4179
- } catch (error) {
4149
+ } catch (_error) {
4180
4150
  logger.warn(`Could not fetch doc from GitHub: ${uri}`);
4181
4151
  }
4182
4152
  }
@@ -4282,7 +4252,7 @@ async function getCode(uri) {
4282
4252
  return file.content;
4283
4253
  }
4284
4254
  }
4285
- } catch (error) {
4255
+ } catch (_error) {
4286
4256
  logger.warn(`Could not fetch code from GitHub: ${uri}`);
4287
4257
  }
4288
4258
  }
@@ -5198,7 +5168,8 @@ async function requestCompletion(messages, options = {}) {
5198
5168
  );
5199
5169
  markSamplingFailed();
5200
5170
  throw new Error(
5201
- "Sampling not supported by this client - use Claude Desktop for this feature"
5171
+ "Sampling not supported by this client - use Claude Desktop for this feature",
5172
+ { cause: error }
5202
5173
  );
5203
5174
  }
5204
5175
  throw error;
@@ -5880,7 +5851,10 @@ async function checkForUpdates() {
5880
5851
  lastChecked: Date.now()
5881
5852
  };
5882
5853
  }
5883
- } catch {
5854
+ } catch (error) {
5855
+ logger.debug("Version check failed (non-blocking)", {
5856
+ error: error instanceof Error ? error.message : String(error)
5857
+ });
5884
5858
  }
5885
5859
  }
5886
5860
  function maybeCheckForUpdates() {
@@ -5889,7 +5863,10 @@ function maybeCheckForUpdates() {
5889
5863
  toolCallCount = 0;
5890
5864
  const fiveMinutes = 5 * 60 * 1e3;
5891
5865
  if (Date.now() - versionCheckResult.lastChecked > fiveMinutes) {
5892
- checkForUpdates().catch(() => {
5866
+ checkForUpdates().catch((error) => {
5867
+ logger.debug("Periodic version check failed", {
5868
+ error: error instanceof Error ? error.message : String(error)
5869
+ });
5893
5870
  });
5894
5871
  }
5895
5872
  }
@@ -5956,7 +5933,10 @@ function sendLogToClient(level, loggerName, data) {
5956
5933
  data
5957
5934
  }
5958
5935
  });
5959
- } catch {
5936
+ } catch (error) {
5937
+ logger.debug("Failed to send log notification to client", {
5938
+ error: error instanceof Error ? error.message : String(error)
5939
+ });
5960
5940
  }
5961
5941
  }
5962
5942
  function sendProgressNotification(progressToken, progress, total, message) {
@@ -5971,7 +5951,10 @@ function sendProgressNotification(progressToken, progress, total, message) {
5971
5951
  ...message && { message }
5972
5952
  }
5973
5953
  });
5974
- } catch {
5954
+ } catch (error) {
5955
+ logger.debug("Failed to send progress notification", {
5956
+ error: error instanceof Error ? error.message : String(error)
5957
+ });
5975
5958
  }
5976
5959
  }
5977
5960
  function createServer() {
@@ -6404,7 +6387,10 @@ function setupSampling(server) {
6404
6387
  }
6405
6388
  async function initializeServer() {
6406
6389
  logger.info("Initializing Midnight MCP Server...");
6407
- checkForUpdates().catch(() => {
6390
+ checkForUpdates().catch((error) => {
6391
+ logger.debug("Startup version check failed (non-blocking)", {
6392
+ error: error instanceof Error ? error.message : String(error)
6393
+ });
6408
6394
  });
6409
6395
  try {
6410
6396
  await vectorStore.initialize();
@@ -7874,7 +7860,7 @@ async function getFile(input) {
7874
7860
  );
7875
7861
  }
7876
7862
  let content = file.content;
7877
- let totalLines = content.split("\n").length;
7863
+ const totalLines = content.split("\n").length;
7878
7864
  let lineRange;
7879
7865
  if (input.startLine || input.endLine) {
7880
7866
  const lines = content.split("\n");
@@ -11186,4 +11172,4 @@ export {
11186
11172
  startServer,
11187
11173
  startHttpServer
11188
11174
  };
11189
- //# sourceMappingURL=chunk-TJ7QA5XT.js.map
11175
+ //# sourceMappingURL=chunk-GDUMSS3E.js.map
@@ -245,13 +245,6 @@ var DEFAULT_REPOSITORIES = [
245
245
  patterns: ["**/*.compact", "**/*.ts", "**/*.md"],
246
246
  exclude: ["node_modules/**", "dist/**"]
247
247
  },
248
- {
249
- owner: "Imdavyking",
250
- repo: "zkbadge",
251
- branch: "main",
252
- patterns: ["**/*.compact", "**/*.ts", "**/*.md"],
253
- exclude: ["node_modules/**", "dist/**"]
254
- },
255
248
  // Core Partner - PaimaStudios (Gaming Infrastructure)
256
249
  {
257
250
  owner: "PaimaStudios",
@@ -858,13 +851,15 @@ var GitHubClient = class {
858
851
  * This is safe because malicious patterns could only come from internal config.
859
852
  */
860
853
  filterFilesByPatterns(files, patterns, exclude) {
861
- const matchPattern = (file, pattern) => {
854
+ const compilePattern = (pattern) => {
862
855
  const regexPattern = pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\./g, "\\.");
863
- return new RegExp(`^${regexPattern}$`).test(file);
856
+ return new RegExp(`^${regexPattern}$`);
864
857
  };
858
+ const includeRegexes = patterns.map(compilePattern);
859
+ const excludeRegexes = exclude.map(compilePattern);
865
860
  return files.filter((file) => {
866
- const matchesInclude = patterns.some((p) => matchPattern(file, p));
867
- const matchesExclude = exclude.some((p) => matchPattern(file, p));
861
+ const matchesInclude = includeRegexes.some((re) => re.test(file));
862
+ const matchesExclude = excludeRegexes.some((re) => re.test(file));
868
863
  return matchesInclude && !matchesExclude;
869
864
  });
870
865
  }
@@ -883,11 +878,19 @@ var GitHubClient = class {
883
878
  logger.info(
884
879
  `Found ${filteredFiles.length} matching files in ${owner}/${repo}`
885
880
  );
881
+ const BATCH_SIZE = 5;
886
882
  const files = [];
887
- for (const filePath of filteredFiles) {
888
- const file = await this.getFileContent(owner, repo, filePath, branch);
889
- if (file) {
890
- files.push(file);
883
+ for (let i = 0; i < filteredFiles.length; i += BATCH_SIZE) {
884
+ const batch = filteredFiles.slice(i, i + BATCH_SIZE);
885
+ const results = await Promise.all(
886
+ batch.map(
887
+ (filePath) => this.getFileContent(owner, repo, filePath, branch)
888
+ )
889
+ );
890
+ for (const file of results) {
891
+ if (file) {
892
+ files.push(file);
893
+ }
891
894
  }
892
895
  }
893
896
  return files;
@@ -1232,9 +1235,18 @@ var EmbeddingGenerator = class {
1232
1235
  model: this.model,
1233
1236
  input: text
1234
1237
  });
1238
+ const embedding = response.data[0].embedding;
1239
+ const EXPECTED_DIMENSIONS = 1536;
1240
+ if (!embedding || embedding.length !== EXPECTED_DIMENSIONS) {
1241
+ logger.warn("Unexpected embedding dimensions", {
1242
+ expected: EXPECTED_DIMENSIONS,
1243
+ actual: embedding?.length ?? 0,
1244
+ model: this.model
1245
+ });
1246
+ }
1235
1247
  return {
1236
1248
  text,
1237
- embedding: response.data[0].embedding,
1249
+ embedding,
1238
1250
  model: this.model,
1239
1251
  tokenCount: response.usage?.total_tokens
1240
1252
  };
@@ -1593,7 +1605,7 @@ var releaseTracker = new ReleaseTracker();
1593
1605
 
1594
1606
  // src/utils/health.ts
1595
1607
  var startTime = Date.now();
1596
- var VERSION = "0.2.16";
1608
+ var VERSION = "0.2.17";
1597
1609
  async function checkGitHubAPI() {
1598
1610
  const start = Date.now();
1599
1611
  try {
@@ -1621,7 +1633,7 @@ async function checkGitHubAPI() {
1621
1633
  }
1622
1634
  async function checkVectorStore() {
1623
1635
  try {
1624
- const { vectorStore: vectorStore2 } = await import("./db-72ZOGII3.js");
1636
+ const { vectorStore: vectorStore2 } = await import("./db-SWEQRS2S.js");
1625
1637
  if (vectorStore2) {
1626
1638
  return {
1627
1639
  status: "pass",
@@ -1806,6 +1818,9 @@ var Cache = class {
1806
1818
  if (this.cache.size >= this.options.maxSize) {
1807
1819
  this.evictOldest();
1808
1820
  }
1821
+ if (this.cache.size > 0 && this.cache.size % 100 === 0) {
1822
+ this.prune();
1823
+ }
1809
1824
  const now = Date.now();
1810
1825
  this.cache.set(key, {
1811
1826
  value,
@@ -1917,12 +1932,6 @@ var metadataCache = new Cache({
1917
1932
  maxSize: 100,
1918
1933
  name: "metadata"
1919
1934
  });
1920
- function pruneAllCaches() {
1921
- searchCache.prune();
1922
- fileCache.prune();
1923
- metadataCache.prune();
1924
- }
1925
- setInterval(pruneAllCaches, 5 * 60 * 1e3);
1926
1935
 
1927
1936
  // src/utils/hosted-api.ts
1928
1937
  var API_TIMEOUT = 15e3;
@@ -1991,7 +2000,8 @@ async function makeRequest(url, endpoint, options) {
1991
2000
  } catch (error) {
1992
2001
  if (error instanceof Error && error.name === "AbortError") {
1993
2002
  throw new Error(
1994
- `Request to ${endpoint} timed out after ${API_TIMEOUT / 1e3}s.`
2003
+ `Request to ${endpoint} timed out after ${API_TIMEOUT / 1e3}s.`,
2004
+ { cause: error }
1995
2005
  );
1996
2006
  }
1997
2007
  throw error;
@@ -2065,7 +2075,10 @@ function trackToolCall(tool, success, durationMs, version) {
2065
2075
  apiRequest("/v1/track/tool", {
2066
2076
  method: "POST",
2067
2077
  body: JSON.stringify({ tool, success, durationMs, version })
2068
- }).catch(() => {
2078
+ }).catch((error) => {
2079
+ logger.debug("Tracking call failed (non-blocking)", {
2080
+ error: String(error)
2081
+ });
2069
2082
  });
2070
2083
  }
2071
2084
 
@@ -2096,7 +2109,7 @@ function serialize(data) {
2096
2109
  }
2097
2110
 
2098
2111
  // src/utils/version.ts
2099
- var CURRENT_VERSION = "0.2.16";
2112
+ var CURRENT_VERSION = "0.2.17";
2100
2113
 
2101
2114
  // src/db/vectorStore.ts
2102
2115
  var VectorStore = class {
@@ -2137,9 +2150,23 @@ var VectorStore = class {
2137
2150
  return;
2138
2151
  }
2139
2152
  try {
2140
- const ids = documents.map((d) => d.id);
2141
- const embeddings = documents.map((d) => d.embedding);
2142
- const metadatas = documents.map((d) => ({
2153
+ const validDocuments = documents.filter((d) => {
2154
+ if (!d.embedding || d.embedding.length === 0) {
2155
+ logger.warn("Document missing embedding, skipping", {
2156
+ id: d.id,
2157
+ filePath: d.metadata.filePath
2158
+ });
2159
+ return false;
2160
+ }
2161
+ return true;
2162
+ });
2163
+ if (validDocuments.length === 0) {
2164
+ logger.warn("No documents with valid embeddings to store");
2165
+ return;
2166
+ }
2167
+ const ids = validDocuments.map((d) => d.id);
2168
+ const embeddings = validDocuments.map((d) => d.embedding);
2169
+ const metadatas = validDocuments.map((d) => ({
2143
2170
  repository: d.metadata.repository,
2144
2171
  filePath: d.metadata.filePath,
2145
2172
  language: d.metadata.language,
@@ -2149,14 +2176,14 @@ var VectorStore = class {
2149
2176
  codeName: d.metadata.codeName,
2150
2177
  isPublic: d.metadata.isPublic
2151
2178
  }));
2152
- const documentContents = documents.map((d) => d.content);
2179
+ const documentContents = validDocuments.map((d) => d.content);
2153
2180
  await this.collection.add({
2154
2181
  ids,
2155
2182
  embeddings,
2156
2183
  metadatas,
2157
2184
  documents: documentContents
2158
2185
  });
2159
- logger.debug(`Added ${documents.length} documents to vector store`);
2186
+ logger.debug(`Added ${validDocuments.length} documents to vector store`);
2160
2187
  } catch (error) {
2161
2188
  logger.error("Failed to add documents to vector store", {
2162
2189
  error: String(error)
@@ -2305,4 +2332,4 @@ export {
2305
2332
  serialize,
2306
2333
  CURRENT_VERSION
2307
2334
  };
2308
- //# sourceMappingURL=chunk-P3MTQHP6.js.map
2335
+ //# sourceMappingURL=chunk-S33OTE35.js.map
@@ -0,0 +1,7 @@
1
+ import {
2
+ vectorStore
3
+ } from "./chunk-S33OTE35.js";
4
+ export {
5
+ vectorStore
6
+ };
7
+ //# sourceMappingURL=db-SWEQRS2S.js.map
package/dist/index.js CHANGED
@@ -9,10 +9,10 @@ import {
9
9
  promptDefinitions,
10
10
  startHttpServer,
11
11
  startServer
12
- } from "./chunk-TJ7QA5XT.js";
12
+ } from "./chunk-GDUMSS3E.js";
13
13
  import {
14
14
  logger
15
- } from "./chunk-P3MTQHP6.js";
15
+ } from "./chunk-S33OTE35.js";
16
16
  export {
17
17
  allResources,
18
18
  allTools,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "midnight-mcp",
3
- "version": "0.2.16",
3
+ "version": "0.2.17",
4
4
  "description": "Model Context Protocol Server for Midnight Blockchain Development",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,9 +15,11 @@
15
15
  "dev": "NODE_ENV=development tsup --watch",
16
16
  "test": "vitest",
17
17
  "test:coverage": "vitest --coverage",
18
+ "lint": "eslint src/",
19
+ "lint:fix": "eslint src/ --fix",
18
20
  "format": "prettier --write src/**/*.ts",
19
21
  "prepublishOnly": "npm run typecheck && npm run build",
20
- "ci": "npm run typecheck && npm run build && npm test"
22
+ "ci": "npm run typecheck && npm run lint && npm run build && npm test"
21
23
  },
22
24
  "keywords": [
23
25
  "midnight",
@@ -43,16 +45,19 @@
43
45
  "zod": "^3.22.4"
44
46
  },
45
47
  "devDependencies": {
48
+ "@eslint/js": "^10.0.1",
46
49
  "@types/express": "^5.0.6",
47
50
  "@types/js-yaml": "^4.0.9",
48
51
  "@types/node": "^20.10.0",
49
52
  "@types/tar": "^6.1.13",
50
53
  "@types/yargs": "^17.0.35",
54
+ "eslint": "^10.0.2",
51
55
  "prettier": "^3.1.0",
52
56
  "tar": "^7.5.2",
53
57
  "tsup": "^8.5.1",
54
58
  "tsx": "^4.6.2",
55
59
  "typescript": "^5.3.2",
60
+ "typescript-eslint": "^8.56.1",
56
61
  "vitest": "^1.0.0"
57
62
  },
58
63
  "engines": {
@@ -1,7 +0,0 @@
1
- import {
2
- vectorStore
3
- } from "./chunk-P3MTQHP6.js";
4
- export {
5
- vectorStore
6
- };
7
- //# sourceMappingURL=db-72ZOGII3.js.map