@mcpc-tech/unplugin-dev-inspector-mcp 0.0.37 → 0.0.39

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.
@@ -21,6 +21,7 @@ let _modelcontextprotocol_sdk_inMemory_js = require("@modelcontextprotocol/sdk/i
21
21
  let node_process = require("node:process");
22
22
  node_process = require_chunk.__toESM(node_process);
23
23
  let os = require("os");
24
+ let child_process = require("child_process");
24
25
  let fs = require("fs");
25
26
  fs = require_chunk.__toESM(fs);
26
27
  let http = require("http");
@@ -36,6 +37,7 @@ let url = require("url");
36
37
  let ai = require("ai");
37
38
  let _agentclientprotocol_sdk = require("@agentclientprotocol/sdk");
38
39
  let node_child_process = require("node:child_process");
40
+ let module$1 = require("module");
39
41
  let fs_promises = require("fs/promises");
40
42
 
41
43
  //#region ../../node_modules/.pnpm/@mcpc-tech+cmcp@0.0.15/node_modules/@mcpc-tech/cmcp/index.mjs
@@ -34869,7 +34871,7 @@ var require_snapshot_utils = /* @__PURE__ */ require_chunk.__commonJSMin(((expor
34869
34871
  //#region ../../node_modules/.pnpm/undici@7.16.0/node_modules/undici/lib/mock/snapshot-recorder.js
34870
34872
  var require_snapshot_recorder = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module) => {
34871
34873
  const { writeFile: writeFile$1, readFile: readFile$1, mkdir: mkdir$1 } = require("node:fs/promises");
34872
- const { dirname: dirname$1, resolve } = require("node:path");
34874
+ const { dirname: dirname$2, resolve } = require("node:path");
34873
34875
  const { setTimeout: setTimeout$1, clearTimeout: clearTimeout$1 } = require("node:timers");
34874
34876
  const { InvalidArgumentError, UndiciError } = require_errors$1();
34875
34877
  const { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
@@ -35148,7 +35150,7 @@ var require_snapshot_recorder = /* @__PURE__ */ require_chunk.__commonJSMin(((ex
35148
35150
  const path$3 = filePath || this.#snapshotPath;
35149
35151
  if (!path$3) throw new InvalidArgumentError("Snapshot path is required");
35150
35152
  const resolvedPath = resolve(path$3);
35151
- await mkdir$1(dirname$1(resolvedPath), { recursive: true });
35153
+ await mkdir$1(dirname$2(resolvedPath), { recursive: true });
35152
35154
  const data$1 = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
35153
35155
  hash,
35154
35156
  snapshot
@@ -58015,9 +58017,9 @@ var require_utils$5 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) =>
58015
58017
  var require_execAsync$1 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) => {
58016
58018
  Object.defineProperty(exports, "__esModule", { value: true });
58017
58019
  exports.execAsync = void 0;
58018
- const child_process$1 = require("child_process");
58020
+ const child_process$2 = require("child_process");
58019
58021
  const util$1 = require("util");
58020
- exports.execAsync = util$1.promisify(child_process$1.exec);
58022
+ exports.execAsync = util$1.promisify(child_process$2.exec);
58021
58023
  }));
58022
58024
 
58023
58025
  //#endregion
@@ -66167,9 +66169,9 @@ var require_utils$1 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) =>
66167
66169
  var require_execAsync = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) => {
66168
66170
  Object.defineProperty(exports, "__esModule", { value: true });
66169
66171
  exports.execAsync = void 0;
66170
- const child_process = require("child_process");
66172
+ const child_process$1 = require("child_process");
66171
66173
  const util = require("util");
66172
- exports.execAsync = util.promisify(child_process.exec);
66174
+ exports.execAsync = util.promisify(child_process$1.exec);
66173
66175
  }));
66174
66176
 
66175
66177
  //#endregion
@@ -83445,6 +83447,19 @@ async function getOrCreateMcpClient(defKey, def) {
83445
83447
  mcpClientConnecting.delete(defKey);
83446
83448
  }
83447
83449
  }
83450
+ async function releaseMcpClient(defKey) {
83451
+ const entry = mcpClientPool.get(defKey);
83452
+ if (!entry) return;
83453
+ entry.refCount -= 1;
83454
+ if (entry.refCount <= 0) {
83455
+ mcpClientPool.delete(defKey);
83456
+ try {
83457
+ await entry.client.close();
83458
+ } catch (err) {
83459
+ console.error("Error closing MCP client:", err);
83460
+ }
83461
+ }
83462
+ }
83448
83463
  var cleanupAllPooledClients = async () => {
83449
83464
  const entries = Array.from(mcpClientPool.entries());
83450
83465
  mcpClientPool.clear();
@@ -83502,7 +83517,12 @@ async function composeMcpDepTools(mcpConfig, filterIn) {
83502
83517
  console.error(`Error creating MCP client for ${name}:`, error);
83503
83518
  }
83504
83519
  }
83505
- const cleanupClients = async () => {};
83520
+ const cleanupClients = async () => {
83521
+ await Promise.all(acquiredKeys.map((k) => releaseMcpClient(k)));
83522
+ acquiredKeys.length = 0;
83523
+ Object.keys(allTools).forEach((key) => delete allTools[key]);
83524
+ Object.keys(allClients).forEach((key) => delete allClients[key]);
83525
+ };
83506
83526
  return {
83507
83527
  tools: allTools,
83508
83528
  clients: allClients,
@@ -86304,6 +86324,17 @@ var ComposableMCPServer = class extends _modelcontextprotocol_sdk_server_index_j
86304
86324
  server: this,
86305
86325
  toolNames: Object.keys(allTools)
86306
86326
  });
86327
+ this.onclose = async () => {
86328
+ await cleanupClients();
86329
+ await this.disposePlugins();
86330
+ await this.logger.info(`[${name}] Event: closed - cleaned up dependent clients and plugins`);
86331
+ };
86332
+ this.onerror = async (error) => {
86333
+ await this.logger.error(`[${name}] Event: error - ${error?.stack ?? String(error)}`);
86334
+ await cleanupClients();
86335
+ await this.disposePlugins();
86336
+ await this.logger.info(`[${name}] Action: cleaned up dependent clients and plugins`);
86337
+ };
86307
86338
  const toolNameToDetailList = Object.entries(allTools);
86308
86339
  const publicToolNames = this.getPublicToolNames();
86309
86340
  const hiddenToolNames = this.getHiddenToolNames();
@@ -86816,6 +86847,7 @@ var ConnectionManager = class {
86816
86847
  transport.onclose = () => this.removeTransport(sessionId);
86817
86848
  }
86818
86849
  removeTransport(sessionId) {
86850
+ console.log(`[dev-inspector] [connection-manager] Removing transport: ${sessionId}`);
86819
86851
  delete this.transports[sessionId];
86820
86852
  for (const [_clientId, sessionIds] of this.watchersByClientId) if (sessionIds.has(sessionId)) {
86821
86853
  sessionIds.delete(sessionId);
@@ -86846,6 +86878,7 @@ var ConnectionManager = class {
86846
86878
  }
86847
86879
  sessionsToRemove.push(existingSessionId);
86848
86880
  }
86881
+ if (sessionsToRemove.length > 0) console.log(`[dev-inspector] [connection-manager] Cleaned up ${sessionsToRemove.length} previous sessions for clientId=${clientId} (new session=${newSessionId})`);
86849
86882
  for (const sessionId of sessionsToRemove) sessionIds.delete(sessionId);
86850
86883
  }
86851
86884
  handleInspectorConnection(sessionId) {
@@ -87056,11 +87089,17 @@ async function handleSseConnection(req, res, serverContext, connectionManager) {
87056
87089
  const url$1 = new URL(req.url ?? "", `http://${host}:${port}`);
87057
87090
  const transport = new _modelcontextprotocol_sdk_server_sse_js.SSEServerTransport("/__mcp__/messages", res);
87058
87091
  const sessionId = transport.sessionId;
87059
- const clientId = url$1.searchParams.get("clientId") || "agent";
87092
+ const clientId = url$1.searchParams.get("clientId") || `agent-${sessionId}`;
87060
87093
  const puppetId = url$1.searchParams.get("puppetId") || "inspector";
87094
+ console.log(`[dev-inspector] [sse] New connection request: clientId=${clientId}, puppetId=${puppetId}, sessionId=${sessionId}`);
87061
87095
  connectionManager.registerTransport(sessionId, transport);
87062
- if (clientId === "inspector") connectionManager.handleInspectorConnection(sessionId);
87063
- else connectionManager.handleWatcherConnection(sessionId, clientId, puppetId, transport);
87096
+ if (clientId === "inspector") {
87097
+ console.log(`[dev-inspector] [sse] Handling Inspector connection: ${sessionId}`);
87098
+ connectionManager.handleInspectorConnection(sessionId);
87099
+ } else {
87100
+ console.log(`[dev-inspector] [sse] Handling Watcher connection: ${sessionId} (binding to ${puppetId})`);
87101
+ connectionManager.handleWatcherConnection(sessionId, clientId, puppetId, transport);
87102
+ }
87064
87103
  await mcpServer.connect(transport);
87065
87104
  } catch (error) {
87066
87105
  console.error("Error establishing SSE connection:", error);
@@ -87200,7 +87239,7 @@ function setupInspectorMiddleware(middlewares, config) {
87200
87239
  }
87201
87240
 
87202
87241
  //#endregion
87203
- //#region ../../node_modules/.pnpm/@mcpc-tech+acp-ai-provider@0.1.41/node_modules/@mcpc-tech/acp-ai-provider/index.mjs
87242
+ //#region ../../node_modules/.pnpm/@mcpc-tech+acp-ai-provider@0.1.43/node_modules/@mcpc-tech/acp-ai-provider/index.mjs
87204
87243
  (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
87205
87244
  function formatToolError(toolResult) {
87206
87245
  if (!toolResult || toolResult.length === 0) return "Unknown tool error";
@@ -87643,7 +87682,6 @@ var ACPLanguageModel = class {
87643
87682
  */
87644
87683
  parseToolCall(update$1) {
87645
87684
  if (update$1.sessionUpdate !== "tool_call") throw new Error("Invalid update type for parseToolCall");
87646
- console.log("Parsing tool call update:", JSON.stringify(update$1, null, 2));
87647
87685
  return {
87648
87686
  toolCallId: update$1.toolCallId,
87649
87687
  toolName: update$1.title || update$1.toolCallId,
@@ -87945,11 +87983,6 @@ var ACPLanguageModel = class {
87945
87983
  this.currentThinkingId = null;
87946
87984
  }
87947
87985
  const { toolCallId, toolName, toolInput } = this.parseToolCall(update$1);
87948
- console.log(`Parsing tool call: ${JSON.stringify({
87949
- toolCallId,
87950
- toolName,
87951
- toolInput
87952
- }, null, 2)}`);
87953
87986
  const existingToolCall = this.toolCallsMap.get(toolCallId);
87954
87987
  const hasInput = toolInput && typeof toolInput === "object" && Object.keys(toolInput).length > 0;
87955
87988
  if (!existingToolCall) {
@@ -87976,13 +88009,14 @@ var ACPLanguageModel = class {
87976
88009
  });
87977
88010
  } else if (!existingToolCall.inputAvailable && hasInput) {
87978
88011
  existingToolCall.inputAvailable = true;
88012
+ if (update$1.title && existingToolCall.name !== update$1.title && update$1.title !== toolCallId) existingToolCall.name = update$1.title;
87979
88013
  controller.enqueue({
87980
88014
  type: "tool-call",
87981
88015
  toolCallId,
87982
88016
  toolName: ACP_PROVIDER_AGENT_DYNAMIC_TOOL_NAME2,
87983
88017
  input: JSON.stringify({
87984
88018
  toolCallId,
87985
- toolName,
88019
+ toolName: existingToolCall.name,
87986
88020
  args: toolInput
87987
88021
  })
87988
88022
  });
@@ -88009,13 +88043,14 @@ var ACPLanguageModel = class {
88009
88043
  }
88010
88044
  if (!toolInfo.inputAvailable) {
88011
88045
  toolInfo.inputAvailable = true;
88046
+ if (update$1.title && toolInfo.name !== update$1.title && update$1.title !== toolCallId) toolInfo.name = update$1.title;
88012
88047
  controller.enqueue({
88013
88048
  type: "tool-call",
88014
88049
  toolCallId,
88015
88050
  toolName: ACP_PROVIDER_AGENT_DYNAMIC_TOOL_NAME2,
88016
88051
  input: JSON.stringify({
88017
88052
  toolCallId,
88018
- toolName,
88053
+ toolName: toolInfo.name,
88019
88054
  args: {}
88020
88055
  })
88021
88056
  });
@@ -88041,13 +88076,14 @@ var ACPLanguageModel = class {
88041
88076
  });
88042
88077
  } else if (!toolInfo.inputAvailable) {
88043
88078
  toolInfo.inputAvailable = true;
88079
+ if (update$1.title && toolInfo.name !== update$1.title && update$1.title !== toolCallId) toolInfo.name = update$1.title;
88044
88080
  controller.enqueue({
88045
88081
  type: "tool-call",
88046
88082
  toolCallId,
88047
88083
  toolName: ACP_PROVIDER_AGENT_DYNAMIC_TOOL_NAME2,
88048
88084
  input: JSON.stringify({
88049
88085
  toolCallId,
88050
- toolName,
88086
+ toolName: toolInfo.name,
88051
88087
  args: {}
88052
88088
  })
88053
88089
  });
@@ -88280,9 +88316,52 @@ function createACPProvider(config) {
88280
88316
  return new ACPProvider(config);
88281
88317
  }
88282
88318
 
88319
+ //#endregion
88320
+ //#region src/utils/npm-package.ts
88321
+ /**
88322
+ * Resolve npm package bin entry point
88323
+ * Returns the absolute path to the bin file, or null if resolution fails
88324
+ */
88325
+ function resolveNpmPackageBin(packageName) {
88326
+ try {
88327
+ const packageJsonPath = (0, module$1.createRequire)(require("url").pathToFileURL(__filename).href).resolve(`${packageName}/package.json`);
88328
+ const packageJson = JSON.parse((0, fs.readFileSync)(packageJsonPath, "utf-8"));
88329
+ let binPath;
88330
+ if (typeof packageJson.bin === "string") binPath = packageJson.bin;
88331
+ else if (typeof packageJson.bin === "object") {
88332
+ const binEntries = Object.entries(packageJson.bin);
88333
+ const matchingEntry = binEntries.find(([name]) => name === packageJson.name.split("/").pop());
88334
+ binPath = matchingEntry ? matchingEntry[1] : binEntries[0]?.[1];
88335
+ }
88336
+ if (!binPath) {
88337
+ console.warn(`[dev-inspector] [acp] No bin entry found in ${packageName}/package.json`);
88338
+ return null;
88339
+ }
88340
+ const binFullPath = (0, path.join)((0, path.dirname)(packageJsonPath), binPath);
88341
+ console.log(`[dev-inspector] [acp] Resolved ${packageName} bin to: ${binFullPath}`);
88342
+ return binFullPath;
88343
+ } catch (error) {
88344
+ console.warn(`[dev-inspector] [acp] Failed to resolve npm package ${packageName}:`, error);
88345
+ return null;
88346
+ }
88347
+ }
88348
+
88283
88349
  //#endregion
88284
88350
  //#region src/middleware/acp-middleware.ts
88285
88351
  /**
88352
+ * Check if a command exists in the system PATH
88353
+ * Skips check for npx since it always exists
88354
+ */
88355
+ function checkCommandExists(command) {
88356
+ if (command === "npx" || command === "node") return true;
88357
+ try {
88358
+ (0, child_process.execSync)(`which ${command}`, { stdio: "ignore" });
88359
+ return true;
88360
+ } catch {
88361
+ return false;
88362
+ }
88363
+ }
88364
+ /**
88286
88365
  * Provider manager - stores one provider per agent config
88287
88366
  * Key: agentKey (command:args), Value: ProviderEntry
88288
88367
  */
@@ -88335,10 +88414,13 @@ async function loadMcpToolsV5(transport) {
88335
88414
  inputSchema: (0, ai.jsonSchema)(toolInfo.inputSchema),
88336
88415
  execute: async (args) => {
88337
88416
  console.log(`[dev-inspector] [acp] Executing MCP tool: ${toolName}`);
88338
- return await callMcpMethodViaTransport(transport, "tools/call", {
88417
+ const result = await callMcpMethodViaTransport(transport, "tools/call", {
88339
88418
  name: toolName,
88340
88419
  arguments: args
88341
88420
  });
88421
+ const parsedResult = _modelcontextprotocol_sdk_types_js.CallToolResultSchema.safeParse(result);
88422
+ if (!parsedResult.success) return result;
88423
+ return parsedResult.data?.content?.map((item) => item?.text).join("\n");
88342
88424
  }
88343
88425
  });
88344
88426
  }
@@ -88346,12 +88428,33 @@ async function loadMcpToolsV5(transport) {
88346
88428
  return tools;
88347
88429
  }
88348
88430
  /**
88349
- * Get the Inspector transport from the connection manager
88431
+ * Default system instructions for DevInspector - provides AI guidance
88432
+ */
88433
+ const DEFAULT_SYSTEM_INSTRUCTIONS = `# DevInspector Context
88434
+
88435
+ You are connected to a web app with DevInspector. Available tools:
88436
+
88437
+ - **list_inspections**: Check pending element inspections from user
88438
+ - **capture_element_context**: Activate visual selector to capture UI elements
88439
+ - **update_inspection_status**: Update inspection status with progress/results
88440
+ - **execute_page_script**: Run JavaScript in browser context
88441
+ - **chrome_devtools**: Access Chrome DevTools for network, console, performance
88442
+
88443
+ Workflow: Check \`list_inspections\` first. If there are pending items, help resolve them. Otherwise, assist with the user's request.`;
88444
+ /**
88445
+ * Get an active transport from the connection manager
88350
88446
  */
88351
88447
  function getActiveTransport() {
88352
88448
  const connectionManager = getConnectionManager();
88353
88449
  if (!connectionManager) return null;
88354
- return connectionManager.getInspectorTransport();
88450
+ return connectionManager.getInspectorTransport() || connectionManager.transports[Object.keys(connectionManager.transports)[0]];
88451
+ }
88452
+ /**
88453
+ * Get specifically the inspector transport for context and tools
88454
+ */
88455
+ function getInspectorTransport() {
88456
+ const connectionManager = getConnectionManager();
88457
+ return connectionManager ? connectionManager.getInspectorTransport() : null;
88355
88458
  }
88356
88459
  function setupAcpMiddleware(middlewares, serverContext, acpOptions) {
88357
88460
  /**
@@ -88398,10 +88501,28 @@ function setupAcpMiddleware(middlewares, serverContext, acpOptions) {
88398
88501
  console.log(`[dev-inspector] [acp] Reusing existing provider for ${agent.name}`);
88399
88502
  provider = providerEntry.provider;
88400
88503
  } else {
88504
+ if (!checkCommandExists(agent.command)) {
88505
+ const hints = [`Agent "${agent.name}" command not found: "${agent.command}"`];
88506
+ if (agent.installCommand) hints.push(`Install with: ${agent.installCommand}`);
88507
+ if (agent.configHint) hints.push(agent.configHint);
88508
+ if (agent.configLink) hints.push(`Documentation: ${agent.configLink}`);
88509
+ console.error(`\n${hints.join("\n")}\n`);
88510
+ return;
88511
+ }
88401
88512
  console.log(`[dev-inspector] [acp] Creating new global provider for ${agent.name}`);
88513
+ let command = agent.command;
88514
+ let args = agent.args;
88515
+ if (agent.npmPackage) {
88516
+ const binPath = resolveNpmPackageBin(agent.npmPackage);
88517
+ if (binPath) {
88518
+ command = binPath;
88519
+ args = agent.npmArgs || [];
88520
+ console.log(`[dev-inspector] [acp] Using resolved npm package: ${agent.npmPackage}`);
88521
+ } else console.log(`[dev-inspector] [acp] Failed to resolve npm package, falling back to: ${agent.command}`);
88522
+ }
88402
88523
  provider = createACPProvider({
88403
- command: agent.command,
88404
- args: agent.args,
88524
+ command,
88525
+ args,
88405
88526
  env: {
88406
88527
  ...process.env,
88407
88528
  ...envVars
@@ -88424,7 +88545,7 @@ function setupAcpMiddleware(middlewares, serverContext, acpOptions) {
88424
88545
  }
88425
88546
  console.log(`[dev-inspector] [acp] Spawning new process/session for ${agent.name}`);
88426
88547
  const initPromise = (async () => {
88427
- const transport = getActiveTransport();
88548
+ const transport = getInspectorTransport() || getActiveTransport();
88428
88549
  let initialTools = {};
88429
88550
  if (transport) try {
88430
88551
  const rawTools = await loadMcpToolsV5(transport);
@@ -88456,6 +88577,7 @@ function setupAcpMiddleware(middlewares, serverContext, acpOptions) {
88456
88577
  res.setHeader("Content-Type", "application/json");
88457
88578
  res.end(JSON.stringify({ sessionId }));
88458
88579
  } catch (error) {
88580
+ if (error instanceof Error && error.message.includes("command not found")) throw error;
88459
88581
  console.error("ACP Init Session Error:", error);
88460
88582
  if (!res.headersSent) {
88461
88583
  res.statusCode = 500;
@@ -88535,9 +88657,19 @@ function setupAcpMiddleware(middlewares, serverContext, acpOptions) {
88535
88657
  shouldCleanupProvider = false;
88536
88658
  } else {
88537
88659
  console.log(`[dev-inspector] [acp] Creating new provider (no session found or provided)`);
88660
+ let command = agent.command;
88661
+ let args = agent.args;
88662
+ if (agent.npmPackage) {
88663
+ const binPath = resolveNpmPackageBin(agent.npmPackage);
88664
+ if (binPath) {
88665
+ command = binPath;
88666
+ args = agent.npmArgs || [];
88667
+ console.log(`[dev-inspector] [acp] Using resolved npm package: ${agent.npmPackage}`);
88668
+ }
88669
+ }
88538
88670
  provider = createACPProvider({
88539
- command: agent.command,
88540
- args: agent.args,
88671
+ command,
88672
+ args,
88541
88673
  env: {
88542
88674
  ...process.env,
88543
88675
  ...envVars
@@ -88550,7 +88682,7 @@ function setupAcpMiddleware(middlewares, serverContext, acpOptions) {
88550
88682
  });
88551
88683
  await provider.initSession();
88552
88684
  }
88553
- const transport = getActiveTransport();
88685
+ const transport = getInspectorTransport() || getActiveTransport();
88554
88686
  let mcpTools = {};
88555
88687
  if (transport) mcpTools = await loadMcpToolsV5(transport);
88556
88688
  else console.warn("[dev-inspector] [acp] No active MCP transport available, tools will not be loaded");
@@ -88567,10 +88699,21 @@ function setupAcpMiddleware(middlewares, serverContext, acpOptions) {
88567
88699
  abortController.abort();
88568
88700
  if (shouldCleanupProvider) provider.cleanup();
88569
88701
  });
88702
+ const systemPrompt = agent.acpSystemPrompt ?? acpOptions?.acpSystemPrompt ?? DEFAULT_SYSTEM_INSTRUCTIONS;
88703
+ const enhancedMessages = (0, ai.convertToModelMessages)(messages).map((msg, index$1) => {
88704
+ if (index$1 === 0 && msg.role === "user" && Array.isArray(msg.content)) return {
88705
+ ...msg,
88706
+ content: [{
88707
+ type: "text",
88708
+ text: `<system_instructions>\n${systemPrompt}\n</system_instructions>\n\n`
88709
+ }, ...msg.content]
88710
+ };
88711
+ return msg;
88712
+ });
88570
88713
  const response = (0, ai.streamText)({
88571
88714
  model: provider.languageModel(model, mode),
88572
88715
  includeRawChunks: true,
88573
- messages: (0, ai.convertToModelMessages)(messages),
88716
+ messages: enhancedMessages,
88574
88717
  abortSignal: abortController.signal,
88575
88718
  tools: acpTools(mcpTools),
88576
88719
  onError: (error) => {