@wisewandtools/mcp-server 2.1.1 → 2.2.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.
package/dist/index.d.ts CHANGED
@@ -311,14 +311,6 @@ declare class WisewandMCPServer {
311
311
  getPromptCount(): number;
312
312
  getHealth(): HealthStatus | null;
313
313
  getMetrics(): Promise<string>;
314
- /**
315
- * Test API connection during startup
316
- * Returns {ok: boolean, error?: string}
317
- */
318
- testAPIConnection(): Promise<{
319
- ok: boolean;
320
- error?: string;
321
- }>;
322
314
  }
323
315
 
324
316
  export { WisewandMCPServer };
package/dist/index.js CHANGED
@@ -134,11 +134,12 @@ var parseConfig = /* @__PURE__ */ __name(() => {
134
134
  strategy: process.env.CACHE_STRATEGY
135
135
  },
136
136
  monitoring: {
137
- enableMetrics: process.env.ENABLE_METRICS !== "false",
137
+ // Default metrics and health check to false in MCP stdio mode (no port binding needed)
138
+ enableMetrics: process.env.ENABLE_METRICS === "true" ? true : process.env.ENABLE_METRICS === "false" ? false : process.env.MCP_MODE !== "stdio",
138
139
  metricsPort: process.env.METRICS_PORT ? parseInt(process.env.METRICS_PORT) : void 0,
139
140
  enableTracing: process.env.ENABLE_TRACING === "true",
140
141
  tracingEndpoint: process.env.TRACING_ENDPOINT,
141
- enableHealthCheck: process.env.ENABLE_HEALTH_CHECK !== "false",
142
+ enableHealthCheck: process.env.ENABLE_HEALTH_CHECK === "true" ? true : process.env.ENABLE_HEALTH_CHECK === "false" ? false : process.env.MCP_MODE !== "stdio",
142
143
  healthCheckInterval: process.env.HEALTH_CHECK_INTERVAL ? parseInt(process.env.HEALTH_CHECK_INTERVAL) : void 0
143
144
  },
144
145
  features: {
@@ -264,13 +265,11 @@ var WisewandAPIClient = class {
264
265
  "User-Agent": "Wisewand-MCP/1.0.0",
265
266
  "Accept": "application/json"
266
267
  };
267
- this.abortController = new AbortController();
268
268
  }
269
269
  static {
270
270
  __name(this, "WisewandAPIClient");
271
271
  }
272
272
  headers;
273
- abortController;
274
273
  /**
275
274
  * Make HTTP request with retry logic
276
275
  */
@@ -278,25 +277,18 @@ var WisewandAPIClient = class {
278
277
  const url = `${this.config.apiUrl}${endpoint}`;
279
278
  return pRetry(
280
279
  async () => {
280
+ const abortController = new AbortController();
281
281
  const timeoutId = setTimeout(() => {
282
- this.abortController.abort();
282
+ abortController.abort();
283
283
  }, this.config.timeout);
284
284
  try {
285
285
  const bodyJSON = body ? JSON.stringify(body) : void 0;
286
- logger.info("API HTTP REQUEST - EXACT JSON", {
287
- method,
288
- endpoint,
289
- url,
290
- bodyJSON,
291
- // The EXACT string being sent over HTTP
292
- body_includes_apply_project_brief_config: bodyJSON?.includes("apply_project_brief_config"),
293
- body_apply_project_brief_config_value: bodyJSON?.match(/"apply_project_brief_config":(true|false)/)?.[1]
294
- });
286
+ logger.debug("API request", { method, endpoint, url });
295
287
  const response = await fetch(url, {
296
288
  method,
297
289
  headers: this.headers,
298
290
  body: bodyJSON,
299
- signal: this.abortController.signal,
291
+ signal: abortController.signal,
300
292
  ...options
301
293
  });
302
294
  clearTimeout(timeoutId);
@@ -310,7 +302,6 @@ var WisewandAPIClient = class {
310
302
  } catch (error) {
311
303
  clearTimeout(timeoutId);
312
304
  if (error instanceof Error && error.name === "AbortError") {
313
- this.abortController = new AbortController();
314
305
  throw new Error(`Request timeout after ${this.config.timeout}ms`);
315
306
  }
316
307
  throw error;
@@ -374,14 +365,17 @@ var WisewandAPIClient = class {
374
365
  articles_type: typeof articles,
375
366
  is_array: Array.isArray(articles),
376
367
  articles_length: Array.isArray(articles) ? articles.length : "N/A",
377
- wrapped_format: { articles },
378
- wrapped_type: typeof { articles },
379
- wrapped_stringified: JSON.stringify({ articles }).substring(0, 200)
368
+ format: "raw_array",
369
+ first_article: articles.length > 0 ? articles[0] : null
380
370
  });
381
- return this.request("POST", "/v1/articles/bulk", { articles });
371
+ const response = await this.request("POST", "/v1/articles/bulk", articles);
372
+ return {
373
+ items: response.ids.map((id) => ({ id })),
374
+ errors: []
375
+ };
382
376
  }
383
377
  async calculateBulkCost(articles) {
384
- return this.request("POST", "/v1/articles/bulk/cost", { articles });
378
+ return this.request("POST", "/v1/articles/bulk/cost", articles);
385
379
  }
386
380
  // ============= Project Operations =============
387
381
  async createProject(input) {
@@ -476,7 +470,7 @@ var WisewandAPIClient = class {
476
470
  return this.request("POST", "/v1/categorypages/cost", input);
477
471
  }
478
472
  async createBulkCategoryPages(items) {
479
- return this.request("POST", "/v1/categorypages/bulk", { items });
473
+ return this.request("POST", "/v1/categorypages/bulk", items);
480
474
  }
481
475
  // ============= Product Pages =============
482
476
  async createProductPage(input) {
@@ -502,7 +496,7 @@ var WisewandAPIClient = class {
502
496
  return this.request("POST", "/v1/productpages/cost", input);
503
497
  }
504
498
  async createBulkProductPages(items) {
505
- return this.request("POST", "/v1/productpages/bulk", { items });
499
+ return this.request("POST", "/v1/productpages/bulk", items);
506
500
  }
507
501
  // ============= Content Discovery =============
508
502
  async discoverArticles(input) {
@@ -528,7 +522,7 @@ var WisewandAPIClient = class {
528
522
  return this.request("POST", "/v1/discoverarticles/cost", input);
529
523
  }
530
524
  async createBulkDiscoverArticles(items) {
531
- return this.request("POST", "/v1/discoverarticles/bulk", { items });
525
+ return this.request("POST", "/v1/discoverarticles/bulk", items);
532
526
  }
533
527
  // ============= Feeds (was RSS Feed Triggers) =============
534
528
  async createFeed(input) {
@@ -614,7 +608,6 @@ var APIError = class extends Error {
614
608
 
615
609
  // src/services/CacheManager.ts
616
610
  import NodeCache from "node-cache";
617
- import Redis from "ioredis";
618
611
  var CacheManager = class {
619
612
  constructor(config2, redisConfig) {
620
613
  this.config = config2;
@@ -641,6 +634,20 @@ var CacheManager = class {
641
634
  });
642
635
  }
643
636
  if ((this.strategy === "redis" || this.strategy === "hybrid") && this.redisConfig) {
637
+ this.initRedis();
638
+ }
639
+ }
640
+ static {
641
+ __name(this, "CacheManager");
642
+ }
643
+ memoryCache;
644
+ redisClient;
645
+ enabled;
646
+ strategy;
647
+ defaultTTL;
648
+ async initRedis() {
649
+ try {
650
+ const { default: Redis } = await import("ioredis");
644
651
  this.redisClient = new Redis({
645
652
  host: this.redisConfig.host,
646
653
  port: this.redisConfig.port,
@@ -661,16 +668,20 @@ var CacheManager = class {
661
668
  this.redisClient.on("error", (error) => {
662
669
  logger.error("Redis cache error", error);
663
670
  });
671
+ } catch (error) {
672
+ logger.warn("Failed to load ioredis, falling back to memory-only cache", { error });
673
+ this.strategy = "memory";
674
+ if (!this.memoryCache) {
675
+ this.memoryCache = new NodeCache({
676
+ stdTTL: this.defaultTTL,
677
+ checkperiod: 120,
678
+ useClones: false,
679
+ deleteOnExpire: true,
680
+ maxKeys: Math.floor(this.config.maxSizeMB * 1e3)
681
+ });
682
+ }
664
683
  }
665
684
  }
666
- static {
667
- __name(this, "CacheManager");
668
- }
669
- memoryCache;
670
- redisClient;
671
- enabled;
672
- strategy;
673
- defaultTTL;
674
685
  async get(key) {
675
686
  if (!this.enabled) return null;
676
687
  try {
@@ -866,8 +877,6 @@ var RateLimiter = class {
866
877
  };
867
878
 
868
879
  // src/monitoring/MetricsCollector.ts
869
- import { Counter, Histogram, Gauge, Registry, collectDefaultMetrics } from "prom-client";
870
- import express from "express";
871
880
  var MetricsCollector = class {
872
881
  static {
873
882
  __name(this, "MetricsCollector");
@@ -875,7 +884,8 @@ var MetricsCollector = class {
875
884
  registry;
876
885
  app;
877
886
  server;
878
- // Metrics
887
+ enabled;
888
+ // Metrics (only initialized when enabled)
879
889
  toolCallCounter;
880
890
  toolCallDuration;
881
891
  apiCallCounter;
@@ -884,116 +894,156 @@ var MetricsCollector = class {
884
894
  cacheMissCounter;
885
895
  activeConnections;
886
896
  errorCounter;
887
- constructor() {
888
- this.registry = new Registry();
889
- collectDefaultMetrics({ register: this.registry });
890
- this.toolCallCounter = new Counter({
891
- name: "mcp_tool_calls_total",
892
- help: "Total number of tool calls",
893
- labelNames: ["tool", "status"],
894
- registers: [this.registry]
895
- });
896
- this.toolCallDuration = new Histogram({
897
- name: "mcp_tool_call_duration_ms",
898
- help: "Tool call duration in milliseconds",
899
- labelNames: ["tool"],
900
- buckets: [10, 50, 100, 500, 1e3, 5e3, 1e4],
901
- registers: [this.registry]
902
- });
903
- this.apiCallCounter = new Counter({
904
- name: "wisewand_api_calls_total",
905
- help: "Total number of Wisewand API calls",
906
- labelNames: ["endpoint", "status"],
907
- registers: [this.registry]
908
- });
909
- this.apiCallDuration = new Histogram({
910
- name: "wisewand_api_call_duration_ms",
911
- help: "Wisewand API call duration in milliseconds",
912
- labelNames: ["endpoint"],
913
- buckets: [100, 500, 1e3, 2e3, 5e3, 1e4, 3e4],
914
- registers: [this.registry]
915
- });
916
- this.cacheHitCounter = new Counter({
917
- name: "cache_hits_total",
918
- help: "Total number of cache hits",
919
- labelNames: ["cache_type"],
920
- registers: [this.registry]
921
- });
922
- this.cacheMissCounter = new Counter({
923
- name: "cache_misses_total",
924
- help: "Total number of cache misses",
925
- labelNames: ["cache_type"],
926
- registers: [this.registry]
927
- });
928
- this.activeConnections = new Gauge({
929
- name: "mcp_active_connections",
930
- help: "Number of active MCP connections",
931
- registers: [this.registry]
932
- });
933
- this.errorCounter = new Counter({
934
- name: "mcp_errors_total",
935
- help: "Total number of errors",
936
- labelNames: ["type", "source"],
937
- registers: [this.registry]
938
- });
939
- logger.info("Metrics collector initialized");
897
+ constructor(enabled = true) {
898
+ this.enabled = enabled;
899
+ if (!this.enabled) {
900
+ logger.info("Metrics collector disabled");
901
+ return;
902
+ }
903
+ }
904
+ async init() {
905
+ if (!this.enabled) return;
906
+ try {
907
+ const { Counter, Histogram, Gauge, Registry, collectDefaultMetrics } = await import("prom-client");
908
+ this.registry = new Registry();
909
+ collectDefaultMetrics({ register: this.registry });
910
+ this.toolCallCounter = new Counter({
911
+ name: "mcp_tool_calls_total",
912
+ help: "Total number of tool calls",
913
+ labelNames: ["tool", "status"],
914
+ registers: [this.registry]
915
+ });
916
+ this.toolCallDuration = new Histogram({
917
+ name: "mcp_tool_call_duration_ms",
918
+ help: "Tool call duration in milliseconds",
919
+ labelNames: ["tool"],
920
+ buckets: [10, 50, 100, 500, 1e3, 5e3, 1e4],
921
+ registers: [this.registry]
922
+ });
923
+ this.apiCallCounter = new Counter({
924
+ name: "wisewand_api_calls_total",
925
+ help: "Total number of Wisewand API calls",
926
+ labelNames: ["endpoint", "status"],
927
+ registers: [this.registry]
928
+ });
929
+ this.apiCallDuration = new Histogram({
930
+ name: "wisewand_api_call_duration_ms",
931
+ help: "Wisewand API call duration in milliseconds",
932
+ labelNames: ["endpoint"],
933
+ buckets: [100, 500, 1e3, 2e3, 5e3, 1e4, 3e4],
934
+ registers: [this.registry]
935
+ });
936
+ this.cacheHitCounter = new Counter({
937
+ name: "cache_hits_total",
938
+ help: "Total number of cache hits",
939
+ labelNames: ["cache_type"],
940
+ registers: [this.registry]
941
+ });
942
+ this.cacheMissCounter = new Counter({
943
+ name: "cache_misses_total",
944
+ help: "Total number of cache misses",
945
+ labelNames: ["cache_type"],
946
+ registers: [this.registry]
947
+ });
948
+ this.activeConnections = new Gauge({
949
+ name: "mcp_active_connections",
950
+ help: "Number of active MCP connections",
951
+ registers: [this.registry]
952
+ });
953
+ this.errorCounter = new Counter({
954
+ name: "mcp_errors_total",
955
+ help: "Total number of errors",
956
+ labelNames: ["type", "source"],
957
+ registers: [this.registry]
958
+ });
959
+ logger.info("Metrics collector initialized");
960
+ } catch (error) {
961
+ logger.warn("Failed to initialize metrics collector, disabling", { error });
962
+ this.enabled = false;
963
+ }
940
964
  }
941
965
  recordToolCall(tool, status, durationMs) {
942
- this.toolCallCounter.inc({ tool, status });
943
- this.toolCallDuration.observe({ tool }, durationMs);
966
+ if (!this.enabled) return;
967
+ this.toolCallCounter?.inc({ tool, status });
968
+ this.toolCallDuration?.observe({ tool }, durationMs);
944
969
  }
945
970
  recordAPICall(endpoint, status, durationMs) {
946
- this.apiCallCounter.inc({ endpoint, status });
971
+ if (!this.enabled) return;
972
+ this.apiCallCounter?.inc({ endpoint, status });
947
973
  if (durationMs) {
948
- this.apiCallDuration.observe({ endpoint }, durationMs);
974
+ this.apiCallDuration?.observe({ endpoint }, durationMs);
949
975
  }
950
976
  }
951
977
  recordCacheHit(cacheType = "memory") {
952
- this.cacheHitCounter.inc({ cache_type: cacheType });
978
+ if (!this.enabled) return;
979
+ this.cacheHitCounter?.inc({ cache_type: cacheType });
953
980
  }
954
981
  recordCacheMiss(cacheType = "memory") {
955
- this.cacheMissCounter.inc({ cache_type: cacheType });
982
+ if (!this.enabled) return;
983
+ this.cacheMissCounter?.inc({ cache_type: cacheType });
956
984
  }
957
985
  setActiveConnections(count) {
958
- this.activeConnections.set(count);
986
+ if (!this.enabled) return;
987
+ this.activeConnections?.set(count);
959
988
  }
960
989
  incrementActiveConnections() {
961
- this.activeConnections.inc();
990
+ if (!this.enabled) return;
991
+ this.activeConnections?.inc();
962
992
  }
963
993
  decrementActiveConnections() {
964
- this.activeConnections.dec();
994
+ if (!this.enabled) return;
995
+ this.activeConnections?.dec();
965
996
  }
966
997
  recordError(type, source) {
967
- this.errorCounter.inc({ type, source });
998
+ if (!this.enabled) return;
999
+ this.errorCounter?.inc({ type, source });
968
1000
  }
969
1001
  async getMetrics() {
1002
+ if (!this.enabled || !this.registry) return "";
970
1003
  return await this.registry.metrics();
971
1004
  }
972
1005
  getMetricsContentType() {
1006
+ if (!this.enabled || !this.registry) return "text/plain";
973
1007
  return this.registry.contentType;
974
1008
  }
975
- startServer(port) {
1009
+ async startServer(port) {
1010
+ if (!this.enabled) return;
976
1011
  if (this.server) {
977
1012
  logger.warn("Metrics server already running");
978
1013
  return;
979
1014
  }
980
- this.app = express();
981
- this.app.get("/health", (_req, res) => {
982
- res.status(200).json({ status: "healthy" });
983
- });
984
- this.app.get("/metrics", async (_req, res) => {
985
- try {
986
- res.set("Content-Type", this.getMetricsContentType());
987
- const metrics = await this.getMetrics();
988
- res.end(metrics);
989
- } catch (error) {
990
- logger.error("Error collecting metrics", error);
991
- res.status(500).end();
1015
+ try {
1016
+ if (!this.registry) {
1017
+ await this.init();
992
1018
  }
993
- });
994
- this.server = this.app.listen(port, () => {
995
- logger.info(`Metrics server started on port ${port}`);
996
- });
1019
+ if (!this.enabled) return;
1020
+ const express = (await import("express")).default;
1021
+ this.app = express();
1022
+ this.app.get("/health", (_req, res) => {
1023
+ res.status(200).json({ status: "healthy" });
1024
+ });
1025
+ this.app.get("/metrics", async (_req, res) => {
1026
+ try {
1027
+ res.set("Content-Type", this.getMetricsContentType());
1028
+ const metrics = await this.getMetrics();
1029
+ res.end(metrics);
1030
+ } catch (error) {
1031
+ logger.error("Error collecting metrics", error);
1032
+ res.status(500).end();
1033
+ }
1034
+ });
1035
+ this.server = this.app.listen(port, () => {
1036
+ logger.info(`Metrics server started on port ${port}`);
1037
+ });
1038
+ this.server.on("error", (error) => {
1039
+ logger.error(`Metrics server failed to bind port ${port}`, { error: error.message, code: error.code });
1040
+ this.server = void 0;
1041
+ this.enabled = false;
1042
+ });
1043
+ } catch (error) {
1044
+ logger.error("Failed to start metrics server", { error });
1045
+ this.enabled = false;
1046
+ }
997
1047
  }
998
1048
  async stopServer() {
999
1049
  if (this.server) {
@@ -1006,6 +1056,7 @@ var MetricsCollector = class {
1006
1056
  }
1007
1057
  }
1008
1058
  reset() {
1059
+ if (!this.enabled || !this.registry) return;
1009
1060
  this.registry.clear();
1010
1061
  }
1011
1062
  };
@@ -6094,7 +6145,7 @@ var WisewandMCPServer = class {
6094
6145
  this.apiClient = new WisewandAPIClient(config2.wisewand);
6095
6146
  this.cacheManager = new CacheManager(config2.cache);
6096
6147
  this.rateLimiter = new RateLimiter(config2.security);
6097
- this.metrics = new MetricsCollector();
6148
+ this.metrics = new MetricsCollector(config2.monitoring.enableMetrics);
6098
6149
  this.healthChecker = new HealthChecker(this.apiClient, this.cacheManager);
6099
6150
  this.articleHandler = new ArticleToolHandler(this.apiClient, this.cacheManager, this.metrics);
6100
6151
  this.projectHandler = new ProjectToolHandler(this.apiClient, this.cacheManager, this.metrics);
@@ -6286,32 +6337,9 @@ var WisewandMCPServer = class {
6286
6337
  getMetrics() {
6287
6338
  return this.metrics.getMetrics();
6288
6339
  }
6289
- /**
6290
- * Test API connection during startup
6291
- * Returns {ok: boolean, error?: string}
6292
- */
6293
- async testAPIConnection() {
6294
- try {
6295
- const health = await this.healthChecker.checkHealth();
6296
- if (health.checks.api) {
6297
- return { ok: true };
6298
- } else {
6299
- return {
6300
- ok: false,
6301
- error: "API health check failed - check API key and network connection"
6302
- };
6303
- }
6304
- } catch (error) {
6305
- return {
6306
- ok: false,
6307
- error: error.message || "Unknown error"
6308
- };
6309
- }
6310
- }
6311
6340
  };
6312
6341
 
6313
6342
  // src/utils/shutdown.ts
6314
- import * as readline from "readline";
6315
6343
  function gracefulShutdown(server) {
6316
6344
  let isShuttingDown = false;
6317
6345
  const shutdown = /* @__PURE__ */ __name(async (signal) => {
@@ -6339,13 +6367,15 @@ function gracefulShutdown(server) {
6339
6367
  process.on("SIGTERM", () => shutdown("SIGTERM"));
6340
6368
  process.on("SIGINT", () => shutdown("SIGINT"));
6341
6369
  process.on("SIGHUP", () => shutdown("SIGHUP"));
6342
- if (process.platform === "win32") {
6343
- const rl = readline.createInterface({
6344
- input: process.stdin,
6345
- output: process.stdout
6346
- });
6347
- rl.on("SIGINT", () => {
6348
- process.emit("SIGINT");
6370
+ if (process.platform === "win32" && process.env.MCP_MODE !== "stdio") {
6371
+ import("readline").then((readline) => {
6372
+ const rl = readline.createInterface({
6373
+ input: process.stdin,
6374
+ output: process.stderr
6375
+ });
6376
+ rl.on("SIGINT", () => {
6377
+ process.emit("SIGINT");
6378
+ });
6349
6379
  });
6350
6380
  }
6351
6381
  }
@@ -6360,23 +6390,6 @@ async function main() {
6360
6390
  nodeVersion: process.version
6361
6391
  });
6362
6392
  const server = new WisewandMCPServer(config);
6363
- logger.info("Testing API connectivity...");
6364
- try {
6365
- const apiHealth = await server.testAPIConnection();
6366
- if (!apiHealth.ok) {
6367
- console.error("\u274C FATAL: Cannot connect to Wisewand API");
6368
- console.error(` Reason: ${apiHealth.error}`);
6369
- console.error(" Check your WISEWAND_API_KEY and network connection");
6370
- console.error(" Get your API key from: https://app.wisewand.ai/api");
6371
- process.exit(1);
6372
- }
6373
- logger.info("\u2713 API connectivity verified");
6374
- } catch (error) {
6375
- console.error("\u274C FATAL: Failed to validate API connection");
6376
- console.error(` Error: ${error.message}`);
6377
- console.error(" Check your WISEWAND_API_KEY and network connection");
6378
- process.exit(1);
6379
- }
6380
6393
  const transport = new StdioServerTransport();
6381
6394
  await server.connect(transport);
6382
6395
  logger.info("Wisewand MCP Server started successfully", {
@@ -6393,11 +6406,9 @@ async function main() {
6393
6406
  __name(main, "main");
6394
6407
  process.on("uncaughtException", (error) => {
6395
6408
  logCriticalError("Uncaught Exception", error);
6396
- process.exit(1);
6397
6409
  });
6398
6410
  process.on("unhandledRejection", (reason) => {
6399
6411
  logCriticalError("Unhandled Rejection", reason);
6400
- process.exit(1);
6401
6412
  });
6402
6413
  main();
6403
6414
  export {