@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 +122 -44
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
5273
|
-
|
|
5274
|
-
}
|
|
5275
|
-
|
|
5276
|
-
|
|
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
|
|
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
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
16606
|
+
appVersion: "1.32.0",
|
|
16529
16607
|
appPlatform: process.platform,
|
|
16530
16608
|
appNodeVersion: process.version,
|
|
16531
16609
|
appInterface: "cli",
|