observability-toolkit 1.8.2 → 1.8.5

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 (259) hide show
  1. package/README.md +60 -0
  2. package/dist/backends/index.d.ts +43 -0
  3. package/dist/backends/index.d.ts.map +1 -1
  4. package/dist/backends/index.js +41 -0
  5. package/dist/backends/index.js.map +1 -1
  6. package/dist/backends/index.test.d.ts +5 -0
  7. package/dist/backends/index.test.d.ts.map +1 -0
  8. package/dist/backends/index.test.js +156 -0
  9. package/dist/backends/index.test.js.map +1 -0
  10. package/dist/backends/local-jsonl-boolean-search.test.js +15 -12
  11. package/dist/backends/local-jsonl-boolean-search.test.js.map +1 -1
  12. package/dist/backends/local-jsonl-cache.test.d.ts +2 -0
  13. package/dist/backends/local-jsonl-cache.test.d.ts.map +1 -0
  14. package/dist/backends/local-jsonl-cache.test.js +295 -0
  15. package/dist/backends/local-jsonl-cache.test.js.map +1 -0
  16. package/dist/backends/local-jsonl-circuit-breaker.test.d.ts +2 -0
  17. package/dist/backends/local-jsonl-circuit-breaker.test.d.ts.map +1 -0
  18. package/dist/backends/local-jsonl-circuit-breaker.test.js +180 -0
  19. package/dist/backends/local-jsonl-circuit-breaker.test.js.map +1 -0
  20. package/dist/backends/local-jsonl-export.test.d.ts +2 -0
  21. package/dist/backends/local-jsonl-export.test.d.ts.map +1 -0
  22. package/dist/backends/local-jsonl-export.test.js +704 -0
  23. package/dist/backends/local-jsonl-export.test.js.map +1 -0
  24. package/dist/backends/local-jsonl-index.test.d.ts +2 -0
  25. package/dist/backends/local-jsonl-index.test.d.ts.map +1 -0
  26. package/dist/backends/local-jsonl-index.test.js +554 -0
  27. package/dist/backends/local-jsonl-index.test.js.map +1 -0
  28. package/dist/backends/local-jsonl-logs.test.d.ts +2 -0
  29. package/dist/backends/local-jsonl-logs.test.d.ts.map +1 -0
  30. package/dist/backends/local-jsonl-logs.test.js +612 -0
  31. package/dist/backends/local-jsonl-logs.test.js.map +1 -0
  32. package/dist/backends/local-jsonl-metrics.test.d.ts +2 -0
  33. package/dist/backends/local-jsonl-metrics.test.d.ts.map +1 -0
  34. package/dist/backends/local-jsonl-metrics.test.js +876 -0
  35. package/dist/backends/local-jsonl-metrics.test.js.map +1 -0
  36. package/dist/backends/local-jsonl-traces.test.d.ts +2 -0
  37. package/dist/backends/local-jsonl-traces.test.d.ts.map +1 -0
  38. package/dist/backends/local-jsonl-traces.test.js +1729 -0
  39. package/dist/backends/local-jsonl-traces.test.js.map +1 -0
  40. package/dist/backends/local-jsonl.d.ts +9 -0
  41. package/dist/backends/local-jsonl.d.ts.map +1 -1
  42. package/dist/backends/local-jsonl.js +348 -227
  43. package/dist/backends/local-jsonl.js.map +1 -1
  44. package/dist/backends/local-jsonl.test.js +290 -21
  45. package/dist/backends/local-jsonl.test.js.map +1 -1
  46. package/dist/backends/signoz-api-circuit-breaker.test.d.ts +6 -0
  47. package/dist/backends/signoz-api-circuit-breaker.test.d.ts.map +1 -0
  48. package/dist/backends/signoz-api-circuit-breaker.test.js +548 -0
  49. package/dist/backends/signoz-api-circuit-breaker.test.js.map +1 -0
  50. package/dist/backends/signoz-api-rate-limiter.test.d.ts +6 -0
  51. package/dist/backends/signoz-api-rate-limiter.test.d.ts.map +1 -0
  52. package/dist/backends/signoz-api-rate-limiter.test.js +389 -0
  53. package/dist/backends/signoz-api-rate-limiter.test.js.map +1 -0
  54. package/dist/backends/signoz-api-ssrf.test.d.ts +6 -0
  55. package/dist/backends/signoz-api-ssrf.test.d.ts.map +1 -0
  56. package/dist/backends/signoz-api-ssrf.test.js +216 -0
  57. package/dist/backends/signoz-api-ssrf.test.js.map +1 -0
  58. package/dist/backends/signoz-api-test-helpers.d.ts +80 -0
  59. package/dist/backends/signoz-api-test-helpers.d.ts.map +1 -0
  60. package/dist/backends/signoz-api-test-helpers.js +79 -0
  61. package/dist/backends/signoz-api-test-helpers.js.map +1 -0
  62. package/dist/backends/signoz-api.d.ts +16 -0
  63. package/dist/backends/signoz-api.d.ts.map +1 -1
  64. package/dist/backends/signoz-api.js +71 -9
  65. package/dist/backends/signoz-api.js.map +1 -1
  66. package/dist/backends/signoz-api.test.d.ts +9 -0
  67. package/dist/backends/signoz-api.test.d.ts.map +1 -1
  68. package/dist/backends/signoz-api.test.js +14 -1027
  69. package/dist/backends/signoz-api.test.js.map +1 -1
  70. package/dist/lib/cache.d.ts +47 -1
  71. package/dist/lib/cache.d.ts.map +1 -1
  72. package/dist/lib/cache.js +40 -3
  73. package/dist/lib/cache.js.map +1 -1
  74. package/dist/lib/circuit-breaker.d.ts +83 -0
  75. package/dist/lib/circuit-breaker.d.ts.map +1 -0
  76. package/dist/lib/circuit-breaker.js +125 -0
  77. package/dist/lib/circuit-breaker.js.map +1 -0
  78. package/dist/lib/circuit-breaker.test.d.ts +2 -0
  79. package/dist/lib/circuit-breaker.test.d.ts.map +1 -0
  80. package/dist/lib/circuit-breaker.test.js +263 -0
  81. package/dist/lib/circuit-breaker.test.js.map +1 -0
  82. package/dist/lib/constants-symlink.test.d.ts +12 -0
  83. package/dist/lib/constants-symlink.test.d.ts.map +1 -0
  84. package/dist/lib/constants-symlink.test.js +357 -0
  85. package/dist/lib/constants-symlink.test.js.map +1 -0
  86. package/dist/lib/constants.d.ts +43 -0
  87. package/dist/lib/constants.d.ts.map +1 -1
  88. package/dist/lib/constants.js +154 -24
  89. package/dist/lib/constants.js.map +1 -1
  90. package/dist/lib/constants.test.js +156 -7
  91. package/dist/lib/constants.test.js.map +1 -1
  92. package/dist/lib/edge-cases.test.d.ts +11 -0
  93. package/dist/lib/edge-cases.test.d.ts.map +1 -0
  94. package/dist/lib/edge-cases.test.js +634 -0
  95. package/dist/lib/edge-cases.test.js.map +1 -0
  96. package/dist/lib/error-sanitizer.d.ts.map +1 -1
  97. package/dist/lib/error-sanitizer.js +62 -26
  98. package/dist/lib/error-sanitizer.js.map +1 -1
  99. package/dist/lib/error-sanitizer.test.js +186 -0
  100. package/dist/lib/error-sanitizer.test.js.map +1 -1
  101. package/dist/lib/error-types.d.ts +54 -0
  102. package/dist/lib/error-types.d.ts.map +1 -0
  103. package/dist/lib/error-types.js +154 -0
  104. package/dist/lib/error-types.js.map +1 -0
  105. package/dist/lib/error-types.test.d.ts +2 -0
  106. package/dist/lib/error-types.test.d.ts.map +1 -0
  107. package/dist/lib/error-types.test.js +196 -0
  108. package/dist/lib/error-types.test.js.map +1 -0
  109. package/dist/lib/file-utils.test.js +3 -3
  110. package/dist/lib/file-utils.test.js.map +1 -1
  111. package/dist/lib/indexer.test.js +157 -24
  112. package/dist/lib/indexer.test.js.map +1 -1
  113. package/dist/lib/input-validator.d.ts +17 -0
  114. package/dist/lib/input-validator.d.ts.map +1 -1
  115. package/dist/lib/input-validator.fuzz.test.d.ts +12 -0
  116. package/dist/lib/input-validator.fuzz.test.d.ts.map +1 -0
  117. package/dist/lib/input-validator.fuzz.test.js +290 -0
  118. package/dist/lib/input-validator.fuzz.test.js.map +1 -0
  119. package/dist/lib/input-validator.js +62 -3
  120. package/dist/lib/input-validator.js.map +1 -1
  121. package/dist/lib/input-validator.test.js +129 -1
  122. package/dist/lib/input-validator.test.js.map +1 -1
  123. package/dist/lib/logger.d.ts +46 -0
  124. package/dist/lib/logger.d.ts.map +1 -0
  125. package/dist/lib/logger.js +81 -0
  126. package/dist/lib/logger.js.map +1 -0
  127. package/dist/lib/logger.test.d.ts +2 -0
  128. package/dist/lib/logger.test.d.ts.map +1 -0
  129. package/dist/lib/logger.test.js +122 -0
  130. package/dist/lib/logger.test.js.map +1 -0
  131. package/dist/lib/query-sanitizer.d.ts +51 -3
  132. package/dist/lib/query-sanitizer.d.ts.map +1 -1
  133. package/dist/lib/query-sanitizer.js +105 -31
  134. package/dist/lib/query-sanitizer.js.map +1 -1
  135. package/dist/lib/query-sanitizer.test.js +102 -1
  136. package/dist/lib/query-sanitizer.test.js.map +1 -1
  137. package/dist/lib/server-utils.d.ts +88 -0
  138. package/dist/lib/server-utils.d.ts.map +1 -0
  139. package/dist/lib/server-utils.js +173 -0
  140. package/dist/lib/server-utils.js.map +1 -0
  141. package/dist/lib/shared-schemas.d.ts +81 -0
  142. package/dist/lib/shared-schemas.d.ts.map +1 -0
  143. package/dist/lib/shared-schemas.js +80 -0
  144. package/dist/lib/shared-schemas.js.map +1 -0
  145. package/dist/lib/shared-schemas.test.d.ts +5 -0
  146. package/dist/lib/shared-schemas.test.d.ts.map +1 -0
  147. package/dist/lib/shared-schemas.test.js +106 -0
  148. package/dist/lib/shared-schemas.test.js.map +1 -0
  149. package/dist/lib/toon-encoder.d.ts +26 -0
  150. package/dist/lib/toon-encoder.d.ts.map +1 -0
  151. package/dist/lib/toon-encoder.js +61 -0
  152. package/dist/lib/toon-encoder.js.map +1 -0
  153. package/dist/lib/toon-encoder.test.d.ts +5 -0
  154. package/dist/lib/toon-encoder.test.d.ts.map +1 -0
  155. package/dist/lib/toon-encoder.test.js +85 -0
  156. package/dist/lib/toon-encoder.test.js.map +1 -0
  157. package/dist/server.d.ts +1 -49
  158. package/dist/server.d.ts.map +1 -1
  159. package/dist/server.js +154 -162
  160. package/dist/server.js.map +1 -1
  161. package/dist/server.test.js +198 -7
  162. package/dist/server.test.js.map +1 -1
  163. package/dist/test-helpers/env-utils.d.ts +87 -0
  164. package/dist/test-helpers/env-utils.d.ts.map +1 -0
  165. package/dist/test-helpers/env-utils.js +132 -0
  166. package/dist/test-helpers/env-utils.js.map +1 -0
  167. package/dist/test-helpers/file-utils.d.ts +67 -0
  168. package/dist/test-helpers/file-utils.d.ts.map +1 -1
  169. package/dist/test-helpers/file-utils.js +165 -2
  170. package/dist/test-helpers/file-utils.js.map +1 -1
  171. package/dist/test-helpers/fuzz-generators.d.ts +58 -0
  172. package/dist/test-helpers/fuzz-generators.d.ts.map +1 -0
  173. package/dist/test-helpers/fuzz-generators.js +216 -0
  174. package/dist/test-helpers/fuzz-generators.js.map +1 -0
  175. package/dist/test-helpers/index.d.ts +11 -0
  176. package/dist/test-helpers/index.d.ts.map +1 -0
  177. package/dist/test-helpers/index.js +30 -0
  178. package/dist/test-helpers/index.js.map +1 -0
  179. package/dist/test-helpers/memfs-utils.d.ts +181 -0
  180. package/dist/test-helpers/memfs-utils.d.ts.map +1 -0
  181. package/dist/test-helpers/memfs-utils.js +292 -0
  182. package/dist/test-helpers/memfs-utils.js.map +1 -0
  183. package/dist/test-helpers/memfs-utils.test.d.ts +5 -0
  184. package/dist/test-helpers/memfs-utils.test.d.ts.map +1 -0
  185. package/dist/test-helpers/memfs-utils.test.js +338 -0
  186. package/dist/test-helpers/memfs-utils.test.js.map +1 -0
  187. package/dist/test-helpers/mock-backends.d.ts +113 -2
  188. package/dist/test-helpers/mock-backends.d.ts.map +1 -1
  189. package/dist/test-helpers/mock-backends.js +199 -3
  190. package/dist/test-helpers/mock-backends.js.map +1 -1
  191. package/dist/test-helpers/mock-backends.test.d.ts +5 -0
  192. package/dist/test-helpers/mock-backends.test.d.ts.map +1 -0
  193. package/dist/test-helpers/mock-backends.test.js +368 -0
  194. package/dist/test-helpers/mock-backends.test.js.map +1 -0
  195. package/dist/test-helpers/race-condition-helpers.d.ts +85 -0
  196. package/dist/test-helpers/race-condition-helpers.d.ts.map +1 -0
  197. package/dist/test-helpers/race-condition-helpers.js +279 -0
  198. package/dist/test-helpers/race-condition-helpers.js.map +1 -0
  199. package/dist/test-helpers/schema-validators.d.ts +32 -0
  200. package/dist/test-helpers/schema-validators.d.ts.map +1 -0
  201. package/dist/test-helpers/schema-validators.js +125 -0
  202. package/dist/test-helpers/schema-validators.js.map +1 -0
  203. package/dist/test-helpers/test-data-builders.d.ts +260 -0
  204. package/dist/test-helpers/test-data-builders.d.ts.map +1 -0
  205. package/dist/test-helpers/test-data-builders.js +337 -0
  206. package/dist/test-helpers/test-data-builders.js.map +1 -0
  207. package/dist/test-helpers/test-data-builders.test.d.ts +2 -0
  208. package/dist/test-helpers/test-data-builders.test.d.ts.map +1 -0
  209. package/dist/test-helpers/test-data-builders.test.js +306 -0
  210. package/dist/test-helpers/test-data-builders.test.js.map +1 -0
  211. package/dist/test-helpers/tool-validators.d.ts +28 -0
  212. package/dist/test-helpers/tool-validators.d.ts.map +1 -0
  213. package/dist/test-helpers/tool-validators.js +71 -0
  214. package/dist/test-helpers/tool-validators.js.map +1 -0
  215. package/dist/tools/context-stats.d.ts +1 -0
  216. package/dist/tools/context-stats.d.ts.map +1 -1
  217. package/dist/tools/context-stats.js +9 -5
  218. package/dist/tools/context-stats.js.map +1 -1
  219. package/dist/tools/context-stats.test.js +24 -10
  220. package/dist/tools/context-stats.test.js.map +1 -1
  221. package/dist/tools/get-trace-url.js +2 -2
  222. package/dist/tools/get-trace-url.js.map +1 -1
  223. package/dist/tools/health-check.js +2 -2
  224. package/dist/tools/health-check.js.map +1 -1
  225. package/dist/tools/query-evaluations.d.ts +21 -18
  226. package/dist/tools/query-evaluations.d.ts.map +1 -1
  227. package/dist/tools/query-evaluations.js +33 -19
  228. package/dist/tools/query-evaluations.js.map +1 -1
  229. package/dist/tools/query-evaluations.test.js +60 -63
  230. package/dist/tools/query-evaluations.test.js.map +1 -1
  231. package/dist/tools/query-llm-events.d.ts +19 -15
  232. package/dist/tools/query-llm-events.d.ts.map +1 -1
  233. package/dist/tools/query-llm-events.js +31 -15
  234. package/dist/tools/query-llm-events.js.map +1 -1
  235. package/dist/tools/query-llm-events.test.js +277 -12
  236. package/dist/tools/query-llm-events.test.js.map +1 -1
  237. package/dist/tools/query-logs.d.ts +22 -22
  238. package/dist/tools/query-logs.d.ts.map +1 -1
  239. package/dist/tools/query-logs.js +9 -9
  240. package/dist/tools/query-logs.js.map +1 -1
  241. package/dist/tools/query-logs.test.js +19 -72
  242. package/dist/tools/query-logs.test.js.map +1 -1
  243. package/dist/tools/query-metrics.d.ts +14 -14
  244. package/dist/tools/query-metrics.d.ts.map +1 -1
  245. package/dist/tools/query-metrics.js +9 -9
  246. package/dist/tools/query-metrics.js.map +1 -1
  247. package/dist/tools/query-metrics.test.js +12 -25
  248. package/dist/tools/query-metrics.test.js.map +1 -1
  249. package/dist/tools/query-traces.d.ts +28 -28
  250. package/dist/tools/query-traces.d.ts.map +1 -1
  251. package/dist/tools/query-traces.js +18 -18
  252. package/dist/tools/query-traces.js.map +1 -1
  253. package/dist/tools/query-traces.test.js +58 -54
  254. package/dist/tools/query-traces.test.js.map +1 -1
  255. package/dist/tools/setup-claudeignore.js +7 -7
  256. package/dist/tools/setup-claudeignore.js.map +1 -1
  257. package/dist/tools/setup-claudeignore.test.js +4 -25
  258. package/dist/tools/setup-claudeignore.test.js.map +1 -1
  259. package/package.json +4 -2
package/dist/server.js CHANGED
@@ -11,112 +11,15 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
11
11
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
12
12
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
13
13
  import { zodToJsonSchema } from 'zod-to-json-schema';
14
+ import { createRequire } from 'module';
14
15
  import { queryTracesTool, queryMetricsTool, queryLogsTool, queryLLMEventsTool, queryEvaluationsTool, healthCheckTool, contextStatsTool, getTraceUrlTool, setupClaudeignoreTool, } from './tools/index.js';
15
16
  import { sanitizeErrorForResponse } from './lib/error-sanitizer.js';
16
- // Rate limiting configuration
17
- const RATE_LIMIT = {
18
- windowMs: 60_000, // 1 minute window
19
- maxRequests: 100, // max requests per window
20
- bucketCount: 60, // 60 buckets for 1-second granularity
21
- };
22
- /**
23
- * Sliding window counter rate limiter with O(1) operations and bounded memory.
24
- * Uses fixed-size bucket array to track request counts per time slice.
25
- *
26
- * Memory usage: O(bucketCount) - fixed regardless of request volume
27
- * Time complexity: O(bucketCount) for sum, but bucketCount is constant (60)
28
- *
29
- * @exported for testing
30
- */
31
- export class RateLimiter {
32
- buckets;
33
- bucketTimestamps;
34
- bucketMs;
35
- windowMs;
36
- maxRequests;
37
- bucketCount;
38
- constructor(windowMs = RATE_LIMIT.windowMs, maxRequests = RATE_LIMIT.maxRequests, bucketCount = RATE_LIMIT.bucketCount) {
39
- this.windowMs = windowMs;
40
- this.maxRequests = maxRequests;
41
- this.bucketCount = bucketCount;
42
- this.bucketMs = Math.floor(windowMs / bucketCount);
43
- // Pre-allocate fixed-size arrays
44
- this.buckets = new Array(bucketCount).fill(0);
45
- this.bucketTimestamps = new Array(bucketCount).fill(0);
46
- }
47
- /**
48
- * Get the bucket index for a given timestamp.
49
- * Uses modulo to create circular buffer behavior.
50
- */
51
- getBucketIndex(timestamp) {
52
- return Math.floor(timestamp / this.bucketMs) % this.bucketCount;
53
- }
54
- /**
55
- * Clear expired buckets and return the current valid count.
56
- * A bucket is expired if its timestamp is older than windowMs ago.
57
- */
58
- cleanAndCount(now) {
59
- const windowStart = now - this.windowMs;
60
- let count = 0;
61
- for (let i = 0; i < this.bucketCount; i++) {
62
- if (this.bucketTimestamps[i] < windowStart) {
63
- // Bucket is expired, reset it
64
- this.buckets[i] = 0;
65
- this.bucketTimestamps[i] = 0;
66
- }
67
- else {
68
- count += this.buckets[i];
69
- }
70
- }
71
- return count;
72
- }
73
- /**
74
- * Check if a request is allowed and record it if so.
75
- * O(bucketCount) time complexity where bucketCount is constant.
76
- */
77
- isAllowed() {
78
- const now = Date.now();
79
- const currentCount = this.cleanAndCount(now);
80
- if (currentCount >= this.maxRequests) {
81
- return false;
82
- }
83
- // Record the request in the current bucket
84
- const bucketIndex = this.getBucketIndex(now);
85
- // If this bucket is from a different time period, reset it
86
- const bucketWindowStart = now - this.windowMs;
87
- if (this.bucketTimestamps[bucketIndex] < bucketWindowStart) {
88
- this.buckets[bucketIndex] = 0;
89
- }
90
- this.buckets[bucketIndex]++;
91
- this.bucketTimestamps[bucketIndex] = now;
92
- return true;
93
- }
94
- /**
95
- * Get the number of remaining allowed requests in the current window.
96
- */
97
- getRemainingRequests() {
98
- const now = Date.now();
99
- const currentCount = this.cleanAndCount(now);
100
- return Math.max(0, this.maxRequests - currentCount);
101
- }
102
- /**
103
- * Get the current memory footprint (for testing).
104
- * Returns the fixed size of internal arrays.
105
- */
106
- getMemoryFootprint() {
107
- return {
108
- bucketCount: this.bucketCount,
109
- arraySize: this.buckets.length + this.bucketTimestamps.length,
110
- };
111
- }
112
- /**
113
- * Reset the rate limiter state (for testing).
114
- */
115
- reset() {
116
- this.buckets.fill(0);
117
- this.bucketTimestamps.fill(0);
118
- }
119
- }
17
+ import { RateLimiter, ServerInitError } from './lib/server-utils.js';
18
+ // Import version from package.json (L1 fix)
19
+ const require = createRequire(import.meta.url);
20
+ const { version: packageVersion } = require('../package.json');
21
+ // Re-export for backwards compatibility
22
+ export { RateLimiter, ServerInitError } from './lib/server-utils.js';
120
23
  const rateLimiter = new RateLimiter();
121
24
  // Tool definitions
122
25
  const tools = [
@@ -142,71 +45,160 @@ function convertZodToJsonSchema(zodSchema) {
142
45
  const { $schema, ...rest } = schema;
143
46
  return rest;
144
47
  }
145
- async function main() {
146
- const server = new Server({
147
- name: 'observability-toolkit',
148
- version: '1.0.0',
149
- }, {
150
- capabilities: {
151
- tools: {},
152
- },
153
- });
154
- // List available tools
155
- server.setRequestHandler(ListToolsRequestSchema, async () => {
156
- return {
157
- tools: tools.map(tool => ({
158
- name: tool.name,
159
- description: tool.description,
160
- inputSchema: convertZodToJsonSchema(tool.inputSchema),
161
- })),
162
- };
163
- });
164
- // Handle tool calls
165
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
166
- const { name, arguments: args } = request.params;
167
- // Rate limiting check
168
- if (!rateLimiter.isAllowed()) {
169
- return {
170
- content: [{ type: 'text', text: `Rate limit exceeded. Please wait before making more requests. Remaining: ${rateLimiter.getRemainingRequests()}` }],
171
- isError: true,
172
- };
48
+ /**
49
+ * Validate that all tools are properly configured before starting the server.
50
+ * @throws ServerInitError if any tool is misconfigured
51
+ */
52
+ function validateTools() {
53
+ if (tools.length === 0) {
54
+ throw new ServerInitError('tool-validation', null, 'No tools registered. Server cannot start without at least one tool.');
55
+ }
56
+ for (const tool of tools) {
57
+ if (!tool.name || typeof tool.name !== 'string') {
58
+ throw new ServerInitError('tool-validation', null, `Tool missing required 'name' property`);
173
59
  }
174
- const tool = tools.find(t => t.name === name);
175
- if (!tool) {
176
- return {
177
- content: [{ type: 'text', text: `Unknown tool: ${name}` }],
178
- isError: true,
179
- };
60
+ if (!tool.handler || typeof tool.handler !== 'function') {
61
+ throw new ServerInitError('tool-validation', null, `Tool '${tool.name}' missing required 'handler' function`);
180
62
  }
181
- try {
182
- // Parse and validate input
183
- const input = tool.inputSchema.parse(args || {});
184
- // Execute handler - use type assertion since we've validated input
185
- const result = await tool.handler(input);
186
- return {
187
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
188
- };
63
+ if (!tool.inputSchema) {
64
+ throw new ServerInitError('tool-validation', null, `Tool '${tool.name}' missing required 'inputSchema'`);
65
+ }
66
+ if (!tool.description || typeof tool.description !== 'string') {
67
+ throw new ServerInitError('tool-validation', null, `Tool '${tool.name}' missing required 'description'`);
189
68
  }
190
- catch (error) {
191
- // Security: sanitize error messages to prevent information disclosure
192
- // - Removes absolute file paths
193
- // - Removes stack traces in production
194
- // - Returns generic messages for internal errors
195
- const errorText = sanitizeErrorForResponse(error);
69
+ }
70
+ }
71
+ async function main() {
72
+ // Step 1: Validate tool configuration
73
+ console.error('[init] Validating tool configuration...');
74
+ try {
75
+ validateTools();
76
+ console.error(`[init] Validated ${tools.length} tools successfully`);
77
+ }
78
+ catch (error) {
79
+ if (error instanceof ServerInitError) {
80
+ throw error;
81
+ }
82
+ throw new ServerInitError('tool-validation', error, 'Failed to validate tool configuration');
83
+ }
84
+ // Step 2: Create MCP server instance
85
+ console.error('[init] Creating MCP server instance...');
86
+ let server;
87
+ try {
88
+ server = new Server({
89
+ name: 'observability-toolkit',
90
+ version: packageVersion,
91
+ }, {
92
+ capabilities: {
93
+ tools: {},
94
+ },
95
+ });
96
+ }
97
+ catch (error) {
98
+ throw new ServerInitError('server-creation', error, 'Failed to create MCP server instance. Check @modelcontextprotocol/sdk installation.');
99
+ }
100
+ // Step 3: Register request handlers
101
+ console.error('[init] Registering request handlers...');
102
+ try {
103
+ // List available tools
104
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
196
105
  return {
197
- content: [{ type: 'text', text: errorText }],
198
- isError: true,
106
+ tools: tools.map(tool => ({
107
+ name: tool.name,
108
+ description: tool.description,
109
+ inputSchema: convertZodToJsonSchema(tool.inputSchema),
110
+ })),
199
111
  };
200
- }
201
- });
202
- // Connect via stdio
203
- const transport = new StdioServerTransport();
204
- await server.connect(transport);
205
- // Log startup to stderr (stdout is for MCP protocol)
206
- console.error('Observability Toolkit MCP server started');
112
+ });
113
+ // Handle tool calls
114
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
115
+ const { name, arguments: args } = request.params;
116
+ // Rate limiting check
117
+ if (!rateLimiter.isAllowed()) {
118
+ const limitedCount = rateLimiter.getRateLimitedCount();
119
+ console.warn(`[rate-limit] Request rejected for tool '${name}' (total rejected: ${limitedCount})`);
120
+ return {
121
+ content: [{ type: 'text', text: `Rate limit exceeded. Please wait before making more requests. Remaining: ${rateLimiter.getRemainingRequests()}` }],
122
+ isError: true,
123
+ };
124
+ }
125
+ const tool = tools.find(t => t.name === name);
126
+ if (!tool) {
127
+ return {
128
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
129
+ isError: true,
130
+ };
131
+ }
132
+ try {
133
+ // Parse and validate input
134
+ const input = tool.inputSchema.parse(args || {});
135
+ // Execute handler - use type assertion since we've validated input
136
+ const result = await tool.handler(input);
137
+ return {
138
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
139
+ };
140
+ }
141
+ catch (error) {
142
+ // Security: sanitize error messages to prevent information disclosure
143
+ // - Removes absolute file paths
144
+ // - Removes stack traces in production
145
+ // - Returns generic messages for internal errors
146
+ const errorText = sanitizeErrorForResponse(error);
147
+ return {
148
+ content: [{ type: 'text', text: errorText }],
149
+ isError: true,
150
+ };
151
+ }
152
+ });
153
+ }
154
+ catch (error) {
155
+ throw new ServerInitError('handler-registration', error, 'Failed to register request handlers');
156
+ }
157
+ // Step 4: Create and connect transport
158
+ console.error('[init] Connecting stdio transport...');
159
+ try {
160
+ const transport = new StdioServerTransport();
161
+ await server.connect(transport);
162
+ }
163
+ catch (error) {
164
+ throw new ServerInitError('transport-connection', error, 'Failed to connect stdio transport. Ensure stdin/stdout are available.');
165
+ }
166
+ // Log startup success to stderr (stdout is for MCP protocol)
167
+ console.error('[init] Observability Toolkit MCP server started successfully');
168
+ console.error(`[init] Available tools: ${tools.map(t => t.name).join(', ')}`);
207
169
  }
208
170
  main().catch((error) => {
209
- console.error('Fatal error:', error);
171
+ if (error instanceof ServerInitError) {
172
+ console.error(`[FATAL] Server initialization failed at step '${error.step}'`);
173
+ console.error(`[FATAL] Error: ${error.message}`);
174
+ if (error.cause) {
175
+ console.error(`[FATAL] Cause: ${error.cause instanceof Error ? error.cause.message : String(error.cause)}`);
176
+ }
177
+ console.error('[FATAL] Please check:');
178
+ switch (error.step) {
179
+ case 'tool-validation':
180
+ console.error(' - All tool modules are properly imported');
181
+ console.error(' - Each tool has name, description, inputSchema, and handler');
182
+ break;
183
+ case 'server-creation':
184
+ console.error(' - @modelcontextprotocol/sdk is installed correctly');
185
+ console.error(' - Run: npm install @modelcontextprotocol/sdk');
186
+ break;
187
+ case 'handler-registration':
188
+ console.error(' - Request schema imports are correct');
189
+ console.error(' - zod-to-json-schema is installed');
190
+ break;
191
+ case 'transport-connection':
192
+ console.error(' - Server is being run with proper stdin/stdout');
193
+ console.error(' - Not running in a detached process');
194
+ break;
195
+ default:
196
+ console.error(' - Check the error message above for details');
197
+ }
198
+ }
199
+ else {
200
+ console.error('[FATAL] Unexpected error during server startup:', error);
201
+ }
210
202
  process.exit(1);
211
203
  });
212
204
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -545,9 +545,10 @@ describe('CallToolRequestSchema handler', () => {
545
545
  });
546
546
  });
547
547
  /**
548
- * Import the RateLimiter for testing
548
+ * Import the RateLimiter and ServerInitError for testing
549
+ * Imported from server-utils to avoid starting the MCP server
549
550
  */
550
- import { RateLimiter } from './server.js';
551
+ import { RateLimiter, ServerInitError } from './lib/server-utils.js';
551
552
  describe('RateLimiter', () => {
552
553
  describe('basic functionality', () => {
553
554
  it('should allow requests under the limit', () => {
@@ -704,11 +705,6 @@ describe('RateLimiter', () => {
704
705
  });
705
706
  });
706
707
  describe('edge cases', () => {
707
- it('should handle zero requests allowed', () => {
708
- const limiter = new RateLimiter(1000, 0, 10);
709
- assert.strictEqual(limiter.isAllowed(), false);
710
- assert.strictEqual(limiter.getRemainingRequests(), 0);
711
- });
712
708
  it('should handle single bucket', () => {
713
709
  const limiter = new RateLimiter(1000, 5, 1);
714
710
  for (let i = 0; i < 5; i++) {
@@ -735,6 +731,34 @@ describe('RateLimiter', () => {
735
731
  assert.strictEqual(blocked, 50);
736
732
  });
737
733
  });
734
+ describe('bounds validation', () => {
735
+ it('should throw error for bucketCount < 1', () => {
736
+ assert.throws(() => new RateLimiter(1000, 10, 0), /bucketCount must be between 1 and/);
737
+ });
738
+ it('should throw error for bucketCount > maxBucketCount', () => {
739
+ assert.throws(() => new RateLimiter(1000, 10, 3601), /bucketCount must be between 1 and/);
740
+ });
741
+ it('should throw error for maxRequests < 1', () => {
742
+ assert.throws(() => new RateLimiter(1000, 0, 10), /maxRequests must be >= 1/);
743
+ });
744
+ it('should throw error for negative maxRequests', () => {
745
+ assert.throws(() => new RateLimiter(1000, -1, 10), /maxRequests must be >= 1/);
746
+ });
747
+ it('should throw error for windowMs < 1', () => {
748
+ assert.throws(() => new RateLimiter(0, 10, 10), /windowMs must be between 1ms and 86400000ms/);
749
+ });
750
+ it('should throw error for windowMs > 86400000 (1 day)', () => {
751
+ assert.throws(() => new RateLimiter(86400001, 10, 10), /windowMs must be between 1ms and 86400000ms/);
752
+ });
753
+ it('should accept valid maximum bucketCount (3600)', () => {
754
+ const limiter = new RateLimiter(3600000, 100, 3600);
755
+ assert.strictEqual(limiter.getMemoryFootprint().bucketCount, 3600);
756
+ });
757
+ it('should accept valid minimum bucketCount (1)', () => {
758
+ const limiter = new RateLimiter(1000, 10, 1);
759
+ assert.strictEqual(limiter.getMemoryFootprint().bucketCount, 1);
760
+ });
761
+ });
738
762
  describe('default configuration', () => {
739
763
  it('should use default values when not specified', () => {
740
764
  const limiter = new RateLimiter();
@@ -745,5 +769,172 @@ describe('RateLimiter', () => {
745
769
  assert.strictEqual(limiter.getRemainingRequests(), 100);
746
770
  });
747
771
  });
772
+ describe('observability', () => {
773
+ it('should track rate-limited requests count', () => {
774
+ const limiter = new RateLimiter(1000, 3, 10);
775
+ // Initially zero
776
+ assert.strictEqual(limiter.getRateLimitedCount(), 0);
777
+ // Use up all requests
778
+ for (let i = 0; i < 3; i++) {
779
+ limiter.isAllowed();
780
+ }
781
+ // Still zero - no rejections yet
782
+ assert.strictEqual(limiter.getRateLimitedCount(), 0);
783
+ // These should be rejected
784
+ limiter.isAllowed();
785
+ assert.strictEqual(limiter.getRateLimitedCount(), 1);
786
+ limiter.isAllowed();
787
+ assert.strictEqual(limiter.getRateLimitedCount(), 2);
788
+ });
789
+ it('should reset rate-limited count on reset', () => {
790
+ const limiter = new RateLimiter(1000, 2, 10);
791
+ // Exhaust limit and trigger rejections
792
+ limiter.isAllowed();
793
+ limiter.isAllowed();
794
+ limiter.isAllowed(); // rejected
795
+ limiter.isAllowed(); // rejected
796
+ assert.strictEqual(limiter.getRateLimitedCount(), 2);
797
+ // Reset should clear counter
798
+ limiter.reset();
799
+ assert.strictEqual(limiter.getRateLimitedCount(), 0);
800
+ });
801
+ });
802
+ });
803
+ describe('ServerInitError', () => {
804
+ describe('constructor', () => {
805
+ it('should create error with step and cause', () => {
806
+ const cause = new Error('Underlying error');
807
+ const error = new ServerInitError('tool-validation', cause);
808
+ assert.strictEqual(error.step, 'tool-validation');
809
+ assert.strictEqual(error.cause, cause);
810
+ assert.strictEqual(error.name, 'ServerInitError');
811
+ assert.ok(error.message.includes('tool-validation'));
812
+ });
813
+ it('should create error with custom message', () => {
814
+ const error = new ServerInitError('server-creation', null, 'Custom error message');
815
+ assert.strictEqual(error.message, 'Custom error message');
816
+ assert.strictEqual(error.step, 'server-creation');
817
+ assert.strictEqual(error.cause, null);
818
+ });
819
+ it('should create error without cause', () => {
820
+ const error = new ServerInitError('tool-validation', undefined);
821
+ assert.strictEqual(error.step, 'tool-validation');
822
+ assert.strictEqual(error.cause, undefined);
823
+ });
824
+ it('should generate default message from step name', () => {
825
+ const error = new ServerInitError('server-creation', null);
826
+ assert.ok(error.message.includes('server-creation'));
827
+ assert.ok(error.message.includes('initialization failed'));
828
+ });
829
+ });
830
+ describe('inheritance', () => {
831
+ it('should be instanceof Error', () => {
832
+ const error = new ServerInitError('tool-validation', null);
833
+ assert.ok(error instanceof Error);
834
+ });
835
+ it('should be instanceof ServerInitError', () => {
836
+ const error = new ServerInitError('tool-validation', null);
837
+ assert.ok(error instanceof ServerInitError);
838
+ });
839
+ it('should have correct name property', () => {
840
+ const error = new ServerInitError('tool-validation', null);
841
+ assert.strictEqual(error.name, 'ServerInitError');
842
+ });
843
+ });
844
+ describe('initialization steps', () => {
845
+ it('should accept tool-validation step', () => {
846
+ const error = new ServerInitError('tool-validation', null, 'Tool validation failed');
847
+ assert.strictEqual(error.step, 'tool-validation');
848
+ });
849
+ it('should accept server-creation step', () => {
850
+ const error = new ServerInitError('server-creation', null, 'Server creation failed');
851
+ assert.strictEqual(error.step, 'server-creation');
852
+ });
853
+ it('should accept handler-registration step', () => {
854
+ const error = new ServerInitError('handler-registration', null, 'Handler registration failed');
855
+ assert.strictEqual(error.step, 'handler-registration');
856
+ });
857
+ it('should accept transport-connection step', () => {
858
+ const error = new ServerInitError('transport-connection', null, 'Transport connection failed');
859
+ assert.strictEqual(error.step, 'transport-connection');
860
+ });
861
+ });
862
+ describe('cause handling', () => {
863
+ it('should preserve Error cause', () => {
864
+ const originalError = new Error('Original problem');
865
+ const error = new ServerInitError('handler-registration', originalError);
866
+ assert.strictEqual(error.cause, originalError);
867
+ assert.strictEqual(error.cause.message, 'Original problem');
868
+ });
869
+ it('should handle string cause', () => {
870
+ const error = new ServerInitError('handler-registration', 'string cause');
871
+ assert.strictEqual(error.cause, 'string cause');
872
+ });
873
+ it('should handle object cause', () => {
874
+ const cause = { code: 'ENOENT', path: '/missing/file' };
875
+ const error = new ServerInitError('handler-registration', cause);
876
+ assert.deepStrictEqual(error.cause, cause);
877
+ });
878
+ it('should handle null cause', () => {
879
+ const error = new ServerInitError('handler-registration', null);
880
+ assert.strictEqual(error.cause, null);
881
+ });
882
+ it('should handle undefined cause', () => {
883
+ const error = new ServerInitError('handler-registration', undefined);
884
+ assert.strictEqual(error.cause, undefined);
885
+ });
886
+ });
887
+ describe('error messaging', () => {
888
+ it('should include step in default message', () => {
889
+ const error = new ServerInitError('transport-connection', null);
890
+ assert.ok(error.message.includes('transport-connection'));
891
+ });
892
+ it('should use custom message when provided', () => {
893
+ const error = new ServerInitError('transport-connection', null, 'Custom: Failed to initialize the widget');
894
+ assert.strictEqual(error.message, 'Custom: Failed to initialize the widget');
895
+ });
896
+ it('should support detailed error messages', () => {
897
+ const error = new ServerInitError('tool-validation', null, "Tool 'obs_query_traces' missing required 'handler' function");
898
+ assert.ok(error.message.includes('obs_query_traces'));
899
+ assert.ok(error.message.includes('handler'));
900
+ });
901
+ });
902
+ describe('error handling patterns', () => {
903
+ it('should work with try-catch', () => {
904
+ let caught = null;
905
+ try {
906
+ throw new ServerInitError('server-creation', null, 'Test error');
907
+ }
908
+ catch (e) {
909
+ if (e instanceof ServerInitError) {
910
+ caught = e;
911
+ }
912
+ }
913
+ assert.ok(caught !== null);
914
+ assert.strictEqual(caught?.step, 'server-creation');
915
+ });
916
+ it('should work with instanceof check', () => {
917
+ const error = new ServerInitError('tool-validation', null);
918
+ if (error instanceof ServerInitError) {
919
+ assert.strictEqual(error.step, 'tool-validation');
920
+ }
921
+ else {
922
+ assert.fail('Should be instanceof ServerInitError');
923
+ }
924
+ });
925
+ it('should serialize to JSON with step and cause', () => {
926
+ const error = new ServerInitError('transport-connection', { code: 123 }, 'Test message');
927
+ const json = JSON.stringify({
928
+ name: error.name,
929
+ message: error.message,
930
+ step: error.step,
931
+ cause: error.cause,
932
+ });
933
+ const parsed = JSON.parse(json);
934
+ assert.strictEqual(parsed.name, 'ServerInitError');
935
+ assert.strictEqual(parsed.step, 'transport-connection');
936
+ assert.deepStrictEqual(parsed.cause, { code: 123 });
937
+ });
938
+ });
748
939
  });
749
940
  //# sourceMappingURL=server.test.js.map