@arabold/docs-mcp-server 1.31.0 → 1.32.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.js CHANGED
@@ -5269,11 +5269,28 @@ class HtmlPlaywrightMiddleware {
5269
5269
  logger.debug(`Could not access content frame for iframe ${index + 1}`);
5270
5270
  return;
5271
5271
  }
5272
- await frame.waitForSelector("body", { timeout: DEFAULT_PAGE_TIMEOUT }).catch(() => {
5273
- logger.debug(`Timeout waiting for body in iframe ${index + 1}`);
5274
- });
5275
- await this.waitForLoadingToComplete(frame);
5276
- const content = await this.extractIframeContent(frame);
5272
+ try {
5273
+ await frame.waitForSelector("body", { timeout: DEFAULT_PAGE_TIMEOUT });
5274
+ } catch {
5275
+ logger.debug(
5276
+ `Timeout waiting for body in iframe ${index + 1} - skipping content extraction`
5277
+ );
5278
+ return;
5279
+ }
5280
+ try {
5281
+ await this.waitForLoadingToComplete(frame);
5282
+ } catch {
5283
+ logger.debug(
5284
+ `Timeout waiting for loading indicators in iframe ${index + 1} - proceeding anyway`
5285
+ );
5286
+ }
5287
+ let content = null;
5288
+ try {
5289
+ content = await this.extractIframeContent(frame);
5290
+ } catch (error) {
5291
+ logger.debug(`Error extracting content from iframe ${index + 1}: ${error}`);
5292
+ return;
5293
+ }
5277
5294
  if (content && content.trim().length > 0) {
5278
5295
  await this.replaceIframeWithContent(page, index, content);
5279
5296
  logger.debug(
@@ -5284,7 +5301,7 @@ class HtmlPlaywrightMiddleware {
5284
5301
  }
5285
5302
  logger.debug(`Successfully loaded iframe ${index + 1}: ${src}`);
5286
5303
  } catch (error) {
5287
- logger.debug(`Error waiting for iframe ${index + 1}: ${error}`);
5304
+ logger.debug(`Error processing iframe ${index + 1}: ${error}`);
5288
5305
  }
5289
5306
  }
5290
5307
  /**
@@ -5447,23 +5464,31 @@ class HtmlPlaywrightMiddleware {
5447
5464
  origin,
5448
5465
  reqOrigin ?? void 0
5449
5466
  );
5450
- const response = await route.fetch({ headers: headers2 });
5451
- const body = await response.text();
5452
- if (response.status() >= 200 && response.status() < 300 && body.length > 0) {
5453
- const contentSizeBytes = Buffer.byteLength(body, "utf8");
5454
- if (contentSizeBytes <= FETCHER_MAX_CACHE_ITEM_SIZE_BYTES) {
5455
- const contentType = response.headers()["content-type"] || "application/octet-stream";
5456
- HtmlPlaywrightMiddleware.resourceCache.set(reqUrl, { body, contentType });
5457
- logger.debug(
5458
- `Cached ${resourceType}: ${reqUrl} (${contentSizeBytes} bytes, cache size: ${HtmlPlaywrightMiddleware.resourceCache.size})`
5459
- );
5460
- } else {
5461
- logger.debug(
5462
- `Resource too large to cache: ${reqUrl} (${contentSizeBytes} bytes > ${FETCHER_MAX_CACHE_ITEM_SIZE_BYTES} bytes limit)`
5463
- );
5467
+ try {
5468
+ const response = await route.fetch({ headers: headers2 });
5469
+ const body = await response.text();
5470
+ if (response.status() >= 200 && response.status() < 300 && body.length > 0) {
5471
+ const contentSizeBytes = Buffer.byteLength(body, "utf8");
5472
+ if (contentSizeBytes <= FETCHER_MAX_CACHE_ITEM_SIZE_BYTES) {
5473
+ const contentType = response.headers()["content-type"] || "application/octet-stream";
5474
+ HtmlPlaywrightMiddleware.resourceCache.set(reqUrl, { body, contentType });
5475
+ logger.debug(
5476
+ `Cached ${resourceType}: ${reqUrl} (${contentSizeBytes} bytes, cache size: ${HtmlPlaywrightMiddleware.resourceCache.size})`
5477
+ );
5478
+ } else {
5479
+ logger.debug(
5480
+ `Resource too large to cache: ${reqUrl} (${contentSizeBytes} bytes > ${FETCHER_MAX_CACHE_ITEM_SIZE_BYTES} bytes limit)`
5481
+ );
5482
+ }
5464
5483
  }
5484
+ return route.fulfill({ response });
5485
+ } catch (error) {
5486
+ const errorMessage = error instanceof Error ? error.message : String(error);
5487
+ logger.debug(
5488
+ `Network error fetching ${resourceType} ${reqUrl}: ${errorMessage}`
5489
+ );
5490
+ return route.abort("failed");
5465
5491
  }
5466
- return route.fulfill({ response });
5467
5492
  }
5468
5493
  const headers = mergePlaywrightHeaders(
5469
5494
  route.request().headers(),
@@ -5472,7 +5497,13 @@ class HtmlPlaywrightMiddleware {
5472
5497
  origin,
5473
5498
  reqOrigin ?? void 0
5474
5499
  );
5475
- return route.continue({ headers });
5500
+ try {
5501
+ return await route.continue({ headers });
5502
+ } catch (error) {
5503
+ const errorMessage = error instanceof Error ? error.message : String(error);
5504
+ logger.debug(`Network error for ${resourceType} ${reqUrl}: ${errorMessage}`);
5505
+ return route.abort("failed");
5506
+ }
5476
5507
  });
5477
5508
  }
5478
5509
  /**
@@ -5656,23 +5687,31 @@ ${frame.content}
5656
5687
  origin ?? void 0,
5657
5688
  reqOrigin ?? void 0
5658
5689
  );
5659
- const response = await route.fetch({ headers: headers2 });
5660
- const body = await response.text();
5661
- if (response.status() >= 200 && response.status() < 300 && body.length > 0) {
5662
- const contentSizeBytes = Buffer.byteLength(body, "utf8");
5663
- if (contentSizeBytes <= FETCHER_MAX_CACHE_ITEM_SIZE_BYTES) {
5664
- const contentType2 = response.headers()["content-type"] || "application/octet-stream";
5665
- HtmlPlaywrightMiddleware.resourceCache.set(reqUrl, { body, contentType: contentType2 });
5666
- logger.debug(
5667
- `Cached ${resourceType}: ${reqUrl} (${contentSizeBytes} bytes, cache size: ${HtmlPlaywrightMiddleware.resourceCache.size})`
5668
- );
5669
- } else {
5670
- logger.debug(
5671
- `Resource too large to cache: ${reqUrl} (${contentSizeBytes} bytes > ${FETCHER_MAX_CACHE_ITEM_SIZE_BYTES} bytes limit)`
5672
- );
5690
+ try {
5691
+ const response = await route.fetch({ headers: headers2 });
5692
+ const body = await response.text();
5693
+ if (response.status() >= 200 && response.status() < 300 && body.length > 0) {
5694
+ const contentSizeBytes = Buffer.byteLength(body, "utf8");
5695
+ if (contentSizeBytes <= FETCHER_MAX_CACHE_ITEM_SIZE_BYTES) {
5696
+ const contentType2 = response.headers()["content-type"] || "application/octet-stream";
5697
+ HtmlPlaywrightMiddleware.resourceCache.set(reqUrl, { body, contentType: contentType2 });
5698
+ logger.debug(
5699
+ `Cached ${resourceType}: ${reqUrl} (${contentSizeBytes} bytes, cache size: ${HtmlPlaywrightMiddleware.resourceCache.size})`
5700
+ );
5701
+ } else {
5702
+ logger.debug(
5703
+ `Resource too large to cache: ${reqUrl} (${contentSizeBytes} bytes > ${FETCHER_MAX_CACHE_ITEM_SIZE_BYTES} bytes limit)`
5704
+ );
5705
+ }
5673
5706
  }
5707
+ return route.fulfill({ response });
5708
+ } catch (error) {
5709
+ const errorMessage = error instanceof Error ? error.message : String(error);
5710
+ logger.debug(
5711
+ `Network error fetching ${resourceType} ${reqUrl}: ${errorMessage}`
5712
+ );
5713
+ return route.abort("failed");
5674
5714
  }
5675
- return route.fulfill({ response });
5676
5715
  }
5677
5716
  const headers = mergePlaywrightHeaders(
5678
5717
  route.request().headers(),
@@ -5681,7 +5720,13 @@ ${frame.content}
5681
5720
  origin ?? void 0,
5682
5721
  reqOrigin ?? void 0
5683
5722
  );
5684
- return route.continue({ headers });
5723
+ try {
5724
+ return await route.continue({ headers });
5725
+ } catch (error) {
5726
+ const errorMessage = error instanceof Error ? error.message : String(error);
5727
+ logger.debug(`Network error for ${resourceType} ${reqUrl}: ${errorMessage}`);
5728
+ return route.abort("failed");
5729
+ }
5685
5730
  });
5686
5731
  await page.goto(context.source, { waitUntil: "load" });
5687
5732
  await page.waitForSelector("body, frameset", { timeout: DEFAULT_PAGE_TIMEOUT });
@@ -10011,6 +10056,8 @@ async function registerMcpService(server, docService, pipeline, readOnly = false
10011
10056
  const mcpServer = createMcpServerInstance(mcpTools, readOnly);
10012
10057
  const authMiddleware = authManager ? createAuthMiddleware(authManager) : null;
10013
10058
  const sseTransports = {};
10059
+ const heartbeatIntervals = {};
10060
+ const HEARTBEAT_INTERVAL_MS = 3e4;
10014
10061
  server.route({
10015
10062
  method: "GET",
10016
10063
  url: "/sse",
@@ -10022,12 +10069,31 @@ async function registerMcpService(server, docService, pipeline, readOnly = false
10022
10069
  if (telemetry.isEnabled()) {
10023
10070
  logger.info(`🔗 MCP client connected: ${transport.sessionId}`);
10024
10071
  }
10025
- reply.raw.on("close", () => {
10072
+ const heartbeatInterval = setInterval(() => {
10073
+ try {
10074
+ reply.raw.write(": heartbeat\n\n");
10075
+ } catch {
10076
+ clearInterval(heartbeatInterval);
10077
+ delete heartbeatIntervals[transport.sessionId];
10078
+ }
10079
+ }, HEARTBEAT_INTERVAL_MS);
10080
+ heartbeatIntervals[transport.sessionId] = heartbeatInterval;
10081
+ const cleanupConnection = () => {
10082
+ const interval = heartbeatIntervals[transport.sessionId];
10083
+ if (interval) {
10084
+ clearInterval(interval);
10085
+ delete heartbeatIntervals[transport.sessionId];
10086
+ }
10026
10087
  delete sseTransports[transport.sessionId];
10027
10088
  transport.close();
10028
10089
  if (telemetry.isEnabled()) {
10029
10090
  logger.info(`🔗 MCP client disconnected: ${transport.sessionId}`);
10030
10091
  }
10092
+ };
10093
+ reply.raw.on("close", cleanupConnection);
10094
+ reply.raw.on("error", (error) => {
10095
+ logger.debug(`SSE connection error: ${error}`);
10096
+ cleanupConnection();
10031
10097
  });
10032
10098
  await mcpServer.connect(transport);
10033
10099
  } catch (error) {
@@ -10069,10 +10135,15 @@ async function registerMcpService(server, docService, pipeline, readOnly = false
10069
10135
  const requestTransport = new StreamableHTTPServerTransport({
10070
10136
  sessionIdGenerator: void 0
10071
10137
  });
10072
- reply.raw.on("close", () => {
10138
+ const cleanupRequest = () => {
10073
10139
  logger.debug("Streamable HTTP request closed");
10074
10140
  requestTransport.close();
10075
10141
  requestServer.close();
10142
+ };
10143
+ reply.raw.on("close", cleanupRequest);
10144
+ reply.raw.on("error", (error) => {
10145
+ logger.debug(`Streamable HTTP connection error: ${error}`);
10146
+ cleanupRequest();
10076
10147
  });
10077
10148
  await requestServer.connect(requestTransport);
10078
10149
  await requestTransport.handleRequest(request.raw, reply.raw, request.body);
@@ -10085,10 +10156,17 @@ async function registerMcpService(server, docService, pipeline, readOnly = false
10085
10156
  }
10086
10157
  });
10087
10158
  mcpServer._sseTransports = sseTransports;
10159
+ mcpServer._heartbeatIntervals = heartbeatIntervals;
10088
10160
  return mcpServer;
10089
10161
  }
10090
10162
  async function cleanupMcpService(mcpServer) {
10091
10163
  try {
10164
+ const heartbeatIntervals = mcpServer._heartbeatIntervals;
10165
+ if (heartbeatIntervals) {
10166
+ for (const interval of Object.values(heartbeatIntervals)) {
10167
+ clearInterval(interval);
10168
+ }
10169
+ }
10092
10170
  const sseTransports = mcpServer._sseTransports;
10093
10171
  if (sseTransports) {
10094
10172
  for (const transport of Object.values(sseTransports)) {
@@ -10692,7 +10770,7 @@ const Layout = ({
10692
10770
  children,
10693
10771
  eventClientConfig
10694
10772
  }) => {
10695
- const versionString = version || "1.31.0";
10773
+ const versionString = version || "1.32.0";
10696
10774
  const versionInitializer = `versionUpdate({ currentVersion: ${`'${versionString}'`} })`;
10697
10775
  return /* @__PURE__ */ jsxs("html", { lang: "en", children: [
10698
10776
  /* @__PURE__ */ jsxs("head", { children: [
@@ -13004,7 +13082,7 @@ class AppServer {
13004
13082
  try {
13005
13083
  if (telemetry.isEnabled()) {
13006
13084
  telemetry.setGlobalContext({
13007
- appVersion: "1.31.0",
13085
+ appVersion: "1.32.0",
13008
13086
  appPlatform: process.platform,
13009
13087
  appNodeVersion: process.version,
13010
13088
  appServicesEnabled: this.getActiveServicesList(),
@@ -16491,7 +16569,7 @@ function createCliProgram() {
16491
16569
  const commandStartTimes = /* @__PURE__ */ new Map();
16492
16570
  let globalEventBus = null;
16493
16571
  let globalTelemetryService = null;
16494
- program.name("docs-mcp-server").description("Unified CLI, MCP Server, and Web Interface for Docs MCP Server.").version("1.31.0").addOption(
16572
+ program.name("docs-mcp-server").description("Unified CLI, MCP Server, and Web Interface for Docs MCP Server.").version("1.32.0").addOption(
16495
16573
  new Option("--verbose", "Enable verbose (debug) logging").conflicts("silent")
16496
16574
  ).addOption(new Option("--silent", "Disable all logging except errors")).addOption(
16497
16575
  new Option("--telemetry", "Enable telemetry collection").env("DOCS_MCP_TELEMETRY").argParser((value) => {
@@ -16525,7 +16603,7 @@ function createCliProgram() {
16525
16603
  if (shouldEnableTelemetry()) {
16526
16604
  if (telemetry.isEnabled()) {
16527
16605
  telemetry.setGlobalContext({
16528
- appVersion: "1.31.0",
16606
+ appVersion: "1.32.0",
16529
16607
  appPlatform: process.platform,
16530
16608
  appNodeVersion: process.version,
16531
16609
  appInterface: "cli",