midnight-mcp 0.0.1 → 0.0.4

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 (75) hide show
  1. package/README.md +40 -20
  2. package/dist/pipeline/github.d.ts +18 -1
  3. package/dist/pipeline/github.js +132 -20
  4. package/dist/server.js +29 -7
  5. package/dist/tools/health.d.ts +91 -0
  6. package/dist/tools/health.js +91 -0
  7. package/dist/tools/index.d.ts +30 -4
  8. package/dist/tools/index.js +10 -3
  9. package/dist/tools/search.d.ts +3 -45
  10. package/dist/tools/search.js +101 -13
  11. package/dist/utils/cache.d.ts +77 -0
  12. package/dist/utils/cache.js +172 -0
  13. package/dist/utils/errors.d.ts +45 -0
  14. package/dist/utils/errors.js +95 -0
  15. package/dist/utils/health.d.ts +29 -0
  16. package/dist/utils/health.js +132 -0
  17. package/dist/utils/index.d.ts +9 -0
  18. package/dist/utils/index.js +9 -0
  19. package/dist/utils/logger.d.ts +30 -1
  20. package/dist/utils/logger.js +68 -3
  21. package/dist/utils/rate-limit.d.ts +61 -0
  22. package/dist/utils/rate-limit.js +148 -0
  23. package/dist/utils/validation.d.ts +52 -0
  24. package/dist/utils/validation.js +255 -0
  25. package/package.json +3 -2
  26. package/dist/db/index.d.ts.map +0 -1
  27. package/dist/db/index.js.map +0 -1
  28. package/dist/db/vectorStore.d.ts.map +0 -1
  29. package/dist/db/vectorStore.js.map +0 -1
  30. package/dist/index.d.ts.map +0 -1
  31. package/dist/index.js.map +0 -1
  32. package/dist/pipeline/embeddings.d.ts.map +0 -1
  33. package/dist/pipeline/embeddings.js.map +0 -1
  34. package/dist/pipeline/github.d.ts.map +0 -1
  35. package/dist/pipeline/github.js.map +0 -1
  36. package/dist/pipeline/index.d.ts.map +0 -1
  37. package/dist/pipeline/index.js.map +0 -1
  38. package/dist/pipeline/indexer.d.ts.map +0 -1
  39. package/dist/pipeline/indexer.js.map +0 -1
  40. package/dist/pipeline/parser.d.ts.map +0 -1
  41. package/dist/pipeline/parser.js.map +0 -1
  42. package/dist/pipeline/releases.d.ts.map +0 -1
  43. package/dist/pipeline/releases.js.map +0 -1
  44. package/dist/pipeline/repository.d.ts.map +0 -1
  45. package/dist/pipeline/repository.js.map +0 -1
  46. package/dist/prompts/index.d.ts.map +0 -1
  47. package/dist/prompts/index.js.map +0 -1
  48. package/dist/prompts/templates.d.ts.map +0 -1
  49. package/dist/prompts/templates.js.map +0 -1
  50. package/dist/resources/code.d.ts.map +0 -1
  51. package/dist/resources/code.js.map +0 -1
  52. package/dist/resources/docs.d.ts.map +0 -1
  53. package/dist/resources/docs.js.map +0 -1
  54. package/dist/resources/index.d.ts.map +0 -1
  55. package/dist/resources/index.js.map +0 -1
  56. package/dist/resources/schemas.d.ts.map +0 -1
  57. package/dist/resources/schemas.js.map +0 -1
  58. package/dist/scripts/index-repos.d.ts.map +0 -1
  59. package/dist/scripts/index-repos.js.map +0 -1
  60. package/dist/server.d.ts.map +0 -1
  61. package/dist/server.js.map +0 -1
  62. package/dist/tools/analyze.d.ts.map +0 -1
  63. package/dist/tools/analyze.js.map +0 -1
  64. package/dist/tools/index.d.ts.map +0 -1
  65. package/dist/tools/index.js.map +0 -1
  66. package/dist/tools/repository.d.ts.map +0 -1
  67. package/dist/tools/repository.js.map +0 -1
  68. package/dist/tools/search.d.ts.map +0 -1
  69. package/dist/tools/search.js.map +0 -1
  70. package/dist/utils/config.d.ts.map +0 -1
  71. package/dist/utils/config.js.map +0 -1
  72. package/dist/utils/index.d.ts.map +0 -1
  73. package/dist/utils/index.js.map +0 -1
  74. package/dist/utils/logger.d.ts.map +0 -1
  75. package/dist/utils/logger.js.map +0 -1
@@ -62,57 +62,15 @@ export type SearchDocsInput = z.infer<typeof SearchDocsInputSchema>;
62
62
  /**
63
63
  * Search Compact smart contract code and patterns
64
64
  */
65
- export declare function searchCompact(input: SearchCompactInput): Promise<{
66
- results: {
67
- code: string;
68
- relevanceScore: number;
69
- source: {
70
- repository: string;
71
- filePath: string;
72
- lines: string;
73
- };
74
- codeType: string;
75
- name: string;
76
- }[];
77
- totalResults: number;
78
- query: string;
79
- }>;
65
+ export declare function searchCompact(input: SearchCompactInput): Promise<{}>;
80
66
  /**
81
67
  * Search TypeScript SDK code, types, and API implementations
82
68
  */
83
- export declare function searchTypeScript(input: SearchTypeScriptInput): Promise<{
84
- results: {
85
- code: string;
86
- relevanceScore: number;
87
- source: {
88
- repository: string;
89
- filePath: string;
90
- lines: string;
91
- };
92
- codeType: string;
93
- name: string;
94
- isExported: boolean;
95
- }[];
96
- totalResults: number;
97
- query: string;
98
- }>;
69
+ export declare function searchTypeScript(input: SearchTypeScriptInput): Promise<{}>;
99
70
  /**
100
71
  * Full-text search across official Midnight documentation
101
72
  */
102
- export declare function searchDocs(input: SearchDocsInput): Promise<{
103
- results: {
104
- content: string;
105
- relevanceScore: number;
106
- source: {
107
- repository: string;
108
- filePath: string;
109
- section: string;
110
- };
111
- }[];
112
- totalResults: number;
113
- query: string;
114
- category: "guides" | "api" | "concepts" | "all";
115
- }>;
73
+ export declare function searchDocs(input: SearchDocsInput): Promise<{}>;
116
74
  export declare const searchTools: ({
117
75
  name: string;
118
76
  description: string;
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { vectorStore } from "../db/index.js";
3
- import { logger } from "../utils/index.js";
3
+ import { logger, validateQuery, validateNumber, searchCache, createCacheKey, } from "../utils/index.js";
4
4
  // Schema definitions for tool inputs
5
5
  export const SearchCompactInputSchema = z.object({
6
6
  query: z.string().describe("Natural language search query for Compact code"),
@@ -44,13 +44,39 @@ export const SearchDocsInputSchema = z.object({
44
44
  * Search Compact smart contract code and patterns
45
45
  */
46
46
  export async function searchCompact(input) {
47
- logger.debug("Searching Compact code", { query: input.query });
47
+ // Validate input
48
+ const queryValidation = validateQuery(input.query);
49
+ if (!queryValidation.isValid) {
50
+ return {
51
+ error: "Invalid query",
52
+ details: queryValidation.errors,
53
+ suggestion: "Provide a valid search query with at least 2 characters",
54
+ };
55
+ }
56
+ const limitValidation = validateNumber(input.limit, {
57
+ min: 1,
58
+ max: 50,
59
+ defaultValue: 10,
60
+ });
61
+ const sanitizedQuery = queryValidation.sanitized;
62
+ const limit = limitValidation.value;
63
+ logger.debug("Searching Compact code", {
64
+ query: sanitizedQuery,
65
+ originalQuery: input.query,
66
+ });
67
+ // Check cache first
68
+ const cacheKey = createCacheKey("compact", sanitizedQuery, limit, input.filter?.repository);
69
+ const cached = searchCache.get(cacheKey);
70
+ if (cached) {
71
+ logger.debug("Search cache hit", { cacheKey });
72
+ return cached;
73
+ }
48
74
  const filter = {
49
75
  language: "compact",
50
76
  ...input.filter,
51
77
  };
52
- const results = await vectorStore.search(input.query, input.limit, filter);
53
- return {
78
+ const results = await vectorStore.search(sanitizedQuery, limit, filter);
79
+ const response = {
54
80
  results: results.map((r) => ({
55
81
  code: r.content,
56
82
  relevanceScore: r.score,
@@ -63,24 +89,53 @@ export async function searchCompact(input) {
63
89
  name: r.metadata.codeName,
64
90
  })),
65
91
  totalResults: results.length,
66
- query: input.query,
92
+ query: sanitizedQuery,
93
+ ...(queryValidation.warnings.length > 0 && {
94
+ warnings: queryValidation.warnings,
95
+ }),
67
96
  };
97
+ // Cache the response
98
+ searchCache.set(cacheKey, response);
99
+ return response;
68
100
  }
69
101
  /**
70
102
  * Search TypeScript SDK code, types, and API implementations
71
103
  */
72
104
  export async function searchTypeScript(input) {
73
- logger.debug("Searching TypeScript code", { query: input.query });
105
+ // Validate input
106
+ const queryValidation = validateQuery(input.query);
107
+ if (!queryValidation.isValid) {
108
+ return {
109
+ error: "Invalid query",
110
+ details: queryValidation.errors,
111
+ suggestion: "Provide a valid search query with at least 2 characters",
112
+ };
113
+ }
114
+ const limitValidation = validateNumber(input.limit, {
115
+ min: 1,
116
+ max: 50,
117
+ defaultValue: 10,
118
+ });
119
+ const sanitizedQuery = queryValidation.sanitized;
120
+ const limit = limitValidation.value;
121
+ logger.debug("Searching TypeScript code", { query: sanitizedQuery });
122
+ // Check cache
123
+ const cacheKey = createCacheKey("typescript", sanitizedQuery, limit, input.includeTypes, input.includeExamples);
124
+ const cached = searchCache.get(cacheKey);
125
+ if (cached) {
126
+ logger.debug("Search cache hit", { cacheKey });
127
+ return cached;
128
+ }
74
129
  const filter = {
75
130
  language: "typescript",
76
131
  };
77
- const results = await vectorStore.search(input.query, input.limit, filter);
132
+ const results = await vectorStore.search(sanitizedQuery, limit, filter);
78
133
  // Filter based on type preferences
79
134
  let filteredResults = results;
80
135
  if (!input.includeTypes) {
81
136
  filteredResults = results.filter((r) => r.metadata.codeType !== "type" && r.metadata.codeType !== "interface");
82
137
  }
83
- return {
138
+ const response = {
84
139
  results: filteredResults.map((r) => ({
85
140
  code: r.content,
86
141
  relevanceScore: r.score,
@@ -94,14 +149,42 @@ export async function searchTypeScript(input) {
94
149
  isExported: r.metadata.isPublic,
95
150
  })),
96
151
  totalResults: filteredResults.length,
97
- query: input.query,
152
+ query: sanitizedQuery,
153
+ ...(queryValidation.warnings.length > 0 && {
154
+ warnings: queryValidation.warnings,
155
+ }),
98
156
  };
157
+ searchCache.set(cacheKey, response);
158
+ return response;
99
159
  }
100
160
  /**
101
161
  * Full-text search across official Midnight documentation
102
162
  */
103
163
  export async function searchDocs(input) {
104
- logger.debug("Searching documentation", { query: input.query });
164
+ // Validate input
165
+ const queryValidation = validateQuery(input.query);
166
+ if (!queryValidation.isValid) {
167
+ return {
168
+ error: "Invalid query",
169
+ details: queryValidation.errors,
170
+ suggestion: "Provide a valid search query with at least 2 characters",
171
+ };
172
+ }
173
+ const limitValidation = validateNumber(input.limit, {
174
+ min: 1,
175
+ max: 50,
176
+ defaultValue: 10,
177
+ });
178
+ const sanitizedQuery = queryValidation.sanitized;
179
+ const limit = limitValidation.value;
180
+ logger.debug("Searching documentation", { query: sanitizedQuery });
181
+ // Check cache
182
+ const cacheKey = createCacheKey("docs", sanitizedQuery, limit, input.category);
183
+ const cached = searchCache.get(cacheKey);
184
+ if (cached) {
185
+ logger.debug("Search cache hit", { cacheKey });
186
+ return cached;
187
+ }
105
188
  const filter = {
106
189
  language: "markdown",
107
190
  };
@@ -110,8 +193,8 @@ export async function searchDocs(input) {
110
193
  // Docs are typically in the midnight-docs repo
111
194
  filter.repository = "midnightntwrk/midnight-docs";
112
195
  }
113
- const results = await vectorStore.search(input.query, input.limit, filter);
114
- return {
196
+ const results = await vectorStore.search(sanitizedQuery, limit, filter);
197
+ const response = {
115
198
  results: results.map((r) => ({
116
199
  content: r.content,
117
200
  relevanceScore: r.score,
@@ -122,9 +205,14 @@ export async function searchDocs(input) {
122
205
  },
123
206
  })),
124
207
  totalResults: results.length,
125
- query: input.query,
208
+ query: sanitizedQuery,
126
209
  category: input.category,
210
+ ...(queryValidation.warnings.length > 0 && {
211
+ warnings: queryValidation.warnings,
212
+ }),
127
213
  };
214
+ searchCache.set(cacheKey, response);
215
+ return response;
128
216
  }
129
217
  // Tool definitions for MCP
130
218
  export const searchTools = [
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Generic caching utilities for MCP server
3
+ * Provides TTL-based caching with memory management
4
+ */
5
+ export interface CacheOptions {
6
+ ttl: number;
7
+ maxSize?: number;
8
+ name?: string;
9
+ }
10
+ export interface CacheEntry<T> {
11
+ value: T;
12
+ expiresAt: number;
13
+ createdAt: number;
14
+ }
15
+ export interface CacheStats {
16
+ hits: number;
17
+ misses: number;
18
+ size: number;
19
+ hitRate: number;
20
+ }
21
+ /**
22
+ * Generic cache implementation with TTL and size limits
23
+ */
24
+ export declare class Cache<T> {
25
+ private cache;
26
+ private options;
27
+ private stats;
28
+ constructor(options: CacheOptions);
29
+ /**
30
+ * Get a value from the cache
31
+ */
32
+ get(key: string): T | undefined;
33
+ /**
34
+ * Set a value in the cache
35
+ */
36
+ set(key: string, value: T, ttl?: number): void;
37
+ /**
38
+ * Check if a key exists and is not expired
39
+ */
40
+ has(key: string): boolean;
41
+ /**
42
+ * Delete a key from the cache
43
+ */
44
+ delete(key: string): boolean;
45
+ /**
46
+ * Clear all entries from the cache
47
+ */
48
+ clear(): void;
49
+ /**
50
+ * Remove expired entries
51
+ */
52
+ prune(): number;
53
+ /**
54
+ * Evict the oldest entry to make room
55
+ */
56
+ private evictOldest;
57
+ /**
58
+ * Get cache statistics
59
+ */
60
+ getStats(): CacheStats;
61
+ /**
62
+ * Get or set with a factory function
63
+ */
64
+ getOrSet(key: string, factory: () => Promise<T>, ttl?: number): Promise<T>;
65
+ }
66
+ /**
67
+ * Create a cache key from multiple parts
68
+ */
69
+ export declare function createCacheKey(...parts: (string | number | boolean | undefined)[]): string;
70
+ export declare const searchCache: Cache<unknown>;
71
+ export declare const fileCache: Cache<string>;
72
+ export declare const metadataCache: Cache<unknown>;
73
+ /**
74
+ * Prune all caches periodically
75
+ */
76
+ export declare function pruneAllCaches(): void;
77
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Generic caching utilities for MCP server
3
+ * Provides TTL-based caching with memory management
4
+ */
5
+ import { logger } from "./logger.js";
6
+ /**
7
+ * Generic cache implementation with TTL and size limits
8
+ */
9
+ export class Cache {
10
+ cache = new Map();
11
+ options;
12
+ stats = { hits: 0, misses: 0 };
13
+ constructor(options) {
14
+ this.options = {
15
+ ttl: options.ttl,
16
+ maxSize: options.maxSize || 1000,
17
+ name: options.name || "cache",
18
+ };
19
+ }
20
+ /**
21
+ * Get a value from the cache
22
+ */
23
+ get(key) {
24
+ const entry = this.cache.get(key);
25
+ if (!entry) {
26
+ this.stats.misses++;
27
+ return undefined;
28
+ }
29
+ // Check if expired
30
+ if (Date.now() > entry.expiresAt) {
31
+ this.cache.delete(key);
32
+ this.stats.misses++;
33
+ return undefined;
34
+ }
35
+ this.stats.hits++;
36
+ return entry.value;
37
+ }
38
+ /**
39
+ * Set a value in the cache
40
+ */
41
+ set(key, value, ttl) {
42
+ // Enforce size limit
43
+ if (this.cache.size >= this.options.maxSize) {
44
+ this.evictOldest();
45
+ }
46
+ const now = Date.now();
47
+ this.cache.set(key, {
48
+ value,
49
+ expiresAt: now + (ttl || this.options.ttl),
50
+ createdAt: now,
51
+ });
52
+ }
53
+ /**
54
+ * Check if a key exists and is not expired
55
+ */
56
+ has(key) {
57
+ const entry = this.cache.get(key);
58
+ if (!entry)
59
+ return false;
60
+ if (Date.now() > entry.expiresAt) {
61
+ this.cache.delete(key);
62
+ return false;
63
+ }
64
+ return true;
65
+ }
66
+ /**
67
+ * Delete a key from the cache
68
+ */
69
+ delete(key) {
70
+ return this.cache.delete(key);
71
+ }
72
+ /**
73
+ * Clear all entries from the cache
74
+ */
75
+ clear() {
76
+ this.cache.clear();
77
+ logger.debug(`Cache cleared: ${this.options.name}`);
78
+ }
79
+ /**
80
+ * Remove expired entries
81
+ */
82
+ prune() {
83
+ const now = Date.now();
84
+ let pruned = 0;
85
+ for (const [key, entry] of this.cache.entries()) {
86
+ if (now > entry.expiresAt) {
87
+ this.cache.delete(key);
88
+ pruned++;
89
+ }
90
+ }
91
+ if (pruned > 0) {
92
+ logger.debug(`Cache pruned: ${this.options.name}`, { pruned });
93
+ }
94
+ return pruned;
95
+ }
96
+ /**
97
+ * Evict the oldest entry to make room
98
+ */
99
+ evictOldest() {
100
+ let oldestKey = null;
101
+ let oldestTime = Infinity;
102
+ for (const [key, entry] of this.cache.entries()) {
103
+ if (entry.createdAt < oldestTime) {
104
+ oldestTime = entry.createdAt;
105
+ oldestKey = key;
106
+ }
107
+ }
108
+ if (oldestKey) {
109
+ this.cache.delete(oldestKey);
110
+ }
111
+ }
112
+ /**
113
+ * Get cache statistics
114
+ */
115
+ getStats() {
116
+ const total = this.stats.hits + this.stats.misses;
117
+ return {
118
+ hits: this.stats.hits,
119
+ misses: this.stats.misses,
120
+ size: this.cache.size,
121
+ hitRate: total > 0 ? this.stats.hits / total : 0,
122
+ };
123
+ }
124
+ /**
125
+ * Get or set with a factory function
126
+ */
127
+ async getOrSet(key, factory, ttl) {
128
+ const cached = this.get(key);
129
+ if (cached !== undefined) {
130
+ return cached;
131
+ }
132
+ const value = await factory();
133
+ this.set(key, value, ttl);
134
+ return value;
135
+ }
136
+ }
137
+ /**
138
+ * Create a cache key from multiple parts
139
+ */
140
+ export function createCacheKey(...parts) {
141
+ return parts
142
+ .filter((p) => p !== undefined)
143
+ .map((p) => String(p))
144
+ .join(":");
145
+ }
146
+ // Pre-configured caches for common use cases
147
+ export const searchCache = new Cache({
148
+ ttl: 5 * 60 * 1000, // 5 minutes
149
+ maxSize: 500,
150
+ name: "search",
151
+ });
152
+ export const fileCache = new Cache({
153
+ ttl: 10 * 60 * 1000, // 10 minutes
154
+ maxSize: 200,
155
+ name: "file",
156
+ });
157
+ export const metadataCache = new Cache({
158
+ ttl: 15 * 60 * 1000, // 15 minutes
159
+ maxSize: 100,
160
+ name: "metadata",
161
+ });
162
+ /**
163
+ * Prune all caches periodically
164
+ */
165
+ export function pruneAllCaches() {
166
+ searchCache.prune();
167
+ fileCache.prune();
168
+ metadataCache.prune();
169
+ }
170
+ // Auto-prune every 5 minutes
171
+ setInterval(pruneAllCaches, 5 * 60 * 1000);
172
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1,45 @@
1
+ /**
2
+ * User-friendly error messages and error handling utilities
3
+ */
4
+ export declare class MCPError extends Error {
5
+ readonly code: string;
6
+ readonly suggestion?: string;
7
+ readonly details?: Record<string, unknown>;
8
+ constructor(message: string, code: string, suggestion?: string, details?: Record<string, unknown>);
9
+ toJSON(): {
10
+ details?: Record<string, unknown> | undefined;
11
+ error: string;
12
+ code: string;
13
+ suggestion: string | undefined;
14
+ };
15
+ }
16
+ /**
17
+ * Common error codes
18
+ */
19
+ export declare const ErrorCodes: {
20
+ readonly RATE_LIMIT: "RATE_LIMIT_EXCEEDED";
21
+ readonly NOT_FOUND: "RESOURCE_NOT_FOUND";
22
+ readonly NETWORK: "NETWORK_ERROR";
23
+ readonly INVALID_INPUT: "INVALID_INPUT";
24
+ readonly UNKNOWN_REPO: "UNKNOWN_REPOSITORY";
25
+ readonly PARSE_ERROR: "PARSE_ERROR";
26
+ readonly CHROMADB_UNAVAILABLE: "CHROMADB_UNAVAILABLE";
27
+ readonly OPENAI_UNAVAILABLE: "OPENAI_UNAVAILABLE";
28
+ };
29
+ /**
30
+ * Create user-friendly error from various error types
31
+ */
32
+ export declare function createUserError(error: unknown, context?: string): MCPError;
33
+ /**
34
+ * Format error for MCP response
35
+ */
36
+ export declare function formatErrorResponse(error: unknown, context?: string): {
37
+ error: string;
38
+ code: string;
39
+ suggestion?: string;
40
+ };
41
+ /**
42
+ * Wrap a function with error handling
43
+ */
44
+ export declare function withErrorHandling<T extends (...args: unknown[]) => Promise<unknown>>(fn: T, context: string): T;
45
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1,95 @@
1
+ /**
2
+ * User-friendly error messages and error handling utilities
3
+ */
4
+ export class MCPError extends Error {
5
+ code;
6
+ suggestion;
7
+ details;
8
+ constructor(message, code, suggestion, details) {
9
+ super(message);
10
+ this.name = "MCPError";
11
+ this.code = code;
12
+ this.suggestion = suggestion;
13
+ this.details = details;
14
+ }
15
+ toJSON() {
16
+ return {
17
+ error: this.message,
18
+ code: this.code,
19
+ suggestion: this.suggestion,
20
+ ...(this.details && { details: this.details }),
21
+ };
22
+ }
23
+ }
24
+ /**
25
+ * Common error codes
26
+ */
27
+ export const ErrorCodes = {
28
+ RATE_LIMIT: "RATE_LIMIT_EXCEEDED",
29
+ NOT_FOUND: "RESOURCE_NOT_FOUND",
30
+ NETWORK: "NETWORK_ERROR",
31
+ INVALID_INPUT: "INVALID_INPUT",
32
+ UNKNOWN_REPO: "UNKNOWN_REPOSITORY",
33
+ PARSE_ERROR: "PARSE_ERROR",
34
+ CHROMADB_UNAVAILABLE: "CHROMADB_UNAVAILABLE",
35
+ OPENAI_UNAVAILABLE: "OPENAI_UNAVAILABLE",
36
+ };
37
+ /**
38
+ * Create user-friendly error from various error types
39
+ */
40
+ export function createUserError(error, context) {
41
+ const message = error instanceof Error ? error.message : String(error);
42
+ const ctx = context ? ` while ${context}` : "";
43
+ // Rate limit errors
44
+ if (message.includes("rate limit") ||
45
+ message.includes("403") ||
46
+ message.includes("API rate limit")) {
47
+ return new MCPError(`GitHub API rate limit exceeded${ctx}`, ErrorCodes.RATE_LIMIT, "Add GITHUB_TOKEN to your config to increase limits from 60 to 5000 requests/hour. " +
48
+ "Get a token at https://github.com/settings/tokens");
49
+ }
50
+ // Not found errors
51
+ if (message.includes("404") || message.includes("Not Found")) {
52
+ return new MCPError(`Resource not found${ctx}`, ErrorCodes.NOT_FOUND, "Check that the repository, file, or version exists and is publicly accessible.");
53
+ }
54
+ // Network errors
55
+ if (message.includes("network") ||
56
+ message.includes("ECONNREFUSED") ||
57
+ message.includes("ETIMEDOUT") ||
58
+ message.includes("timeout")) {
59
+ return new MCPError(`Network error${ctx}`, ErrorCodes.NETWORK, "Check your internet connection and try again. If the problem persists, " +
60
+ "the service may be temporarily unavailable.");
61
+ }
62
+ // ChromaDB errors
63
+ if (message.includes("chroma") || message.includes("8000")) {
64
+ return new MCPError(`ChromaDB is not available${ctx}`, ErrorCodes.CHROMADB_UNAVAILABLE, "ChromaDB is optional. Without it, search uses keyword matching instead of semantic search. " +
65
+ "To enable semantic search, run: docker run -d -p 8000:8000 chromadb/chroma");
66
+ }
67
+ // OpenAI errors
68
+ if (message.includes("openai") || message.includes("embedding")) {
69
+ return new MCPError(`OpenAI API error${ctx}`, ErrorCodes.OPENAI_UNAVAILABLE, "OpenAI is optional. Without it, search uses keyword matching. " +
70
+ "To enable semantic search, add OPENAI_API_KEY to your config.");
71
+ }
72
+ // Default error
73
+ return new MCPError(`An error occurred${ctx}: ${message}`, "UNKNOWN_ERROR", "If this problem persists, please report it at https://github.com/Olanetsoft/midnight-mcp/issues");
74
+ }
75
+ /**
76
+ * Format error for MCP response
77
+ */
78
+ export function formatErrorResponse(error, context) {
79
+ const mcpError = error instanceof MCPError ? error : createUserError(error, context);
80
+ return mcpError.toJSON();
81
+ }
82
+ /**
83
+ * Wrap a function with error handling
84
+ */
85
+ export function withErrorHandling(fn, context) {
86
+ return (async (...args) => {
87
+ try {
88
+ return await fn(...args);
89
+ }
90
+ catch (error) {
91
+ throw createUserError(error, context);
92
+ }
93
+ });
94
+ }
95
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Health check utilities for MCP server monitoring
3
+ */
4
+ export interface HealthStatus {
5
+ status: "healthy" | "degraded" | "unhealthy";
6
+ timestamp: string;
7
+ version: string;
8
+ uptime: number;
9
+ checks: {
10
+ name: string;
11
+ status: "pass" | "warn" | "fail";
12
+ message?: string;
13
+ latency?: number;
14
+ }[];
15
+ }
16
+ /**
17
+ * Perform a full health check
18
+ */
19
+ export declare function getHealthStatus(): Promise<HealthStatus>;
20
+ /**
21
+ * Get a quick health check (no external calls)
22
+ */
23
+ export declare function getQuickHealthStatus(): Omit<HealthStatus, "checks"> & {
24
+ checks: {
25
+ name: string;
26
+ status: "pass";
27
+ }[];
28
+ };
29
+ //# sourceMappingURL=health.d.ts.map