@mcpc-tech/unplugin-dev-inspector-mcp 0.0.31 → 0.0.34

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/client.d.ts CHANGED
@@ -3,19 +3,65 @@
3
3
 
4
4
  declare module "virtual:dev-inspector-mcp" {
5
5
  /**
6
- * Development-only inspector initialization module.
7
- * This module is automatically tree-shaken in production builds.
8
- *
9
- * Import this in your entry point for non-HTML projects (miniapps, library bundles):
6
+ * JSON Schema for tool input parameters
7
+ */
8
+ export interface ToolInputSchema {
9
+ type: "object";
10
+ properties: Record<
11
+ string,
12
+ {
13
+ type: string;
14
+ description?: string;
15
+ enum?: string[];
16
+ default?: unknown;
17
+ }
18
+ >;
19
+ required?: string[];
20
+ }
21
+
22
+ /**
23
+ * Custom inspector tool definition
24
+ */
25
+ export interface InspectorTool {
26
+ /** Unique tool name */
27
+ name: string;
28
+ /** Tool description for AI agents */
29
+ description: string;
30
+ /** JSON Schema defining input parameters */
31
+ inputSchema: ToolInputSchema;
32
+ /** Implementation function that executes when the tool is called */
33
+ implementation: (args: Record<string, unknown>) => unknown | Promise<unknown>;
34
+ }
35
+
36
+ /**
37
+ * Register a custom tool that can be called by AI agents through MCP.
10
38
  *
11
39
  * @example
12
40
  * ```typescript
13
- * // main.ts
14
- * import 'virtual:dev-inspector-mcp';
15
- * ```
41
+ * import { registerInspectorTool } from 'virtual:dev-inspector-mcp';
16
42
  *
17
- * When building for production, bundlers will statically replace `import.meta.env.DEV`
18
- * with `false`, causing the entire module code to be removed via dead code elimination.
43
+ * registerInspectorTool({
44
+ * name: "custom_alert",
45
+ * description: "Show a custom alert in the browser",
46
+ * inputSchema: {
47
+ * type: "object",
48
+ * properties: {
49
+ * message: { type: "string", description: "Message to display" }
50
+ * },
51
+ * required: ["message"]
52
+ * },
53
+ * implementation: (args) => {
54
+ * alert(args.message as string);
55
+ * return { success: true };
56
+ * }
57
+ * });
58
+ * ```
59
+ */
60
+ export function registerInspectorTool(tool: InspectorTool): void;
61
+
62
+ /**
63
+ * Development-only inspector initialization module.
64
+ * This module is automatically tree-shaken in production builds.
19
65
  */
20
66
  const _default: void;
21
67
  export default _default;
@@ -87226,7 +87226,7 @@ function setupInspectorMiddleware(middlewares, config) {
87226
87226
  }
87227
87227
 
87228
87228
  //#endregion
87229
- //#region ../../node_modules/.pnpm/@mcpc-tech+acp-ai-provider@0.1.37/node_modules/@mcpc-tech/acp-ai-provider/index.mjs
87229
+ //#region ../../node_modules/.pnpm/@mcpc-tech+acp-ai-provider@0.1.41/node_modules/@mcpc-tech/acp-ai-provider/index.mjs
87230
87230
  (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
87231
87231
  function formatToolError(toolResult) {
87232
87232
  if (!toolResult || toolResult.length === 0) return "Unknown tool error";
@@ -87383,8 +87383,13 @@ var ToolProxyHost = class {
87383
87383
  * Start TCP server and return MCP server config for ACP
87384
87384
  */
87385
87385
  async start() {
87386
- if (this.server) throw new Error("Tool proxy already started");
87387
- await this.startServer();
87386
+ if (!this.server) await this.startServer();
87387
+ return this.getServerConfig();
87388
+ }
87389
+ /**
87390
+ * Get MCP server configuration
87391
+ */
87392
+ getServerConfig() {
87388
87393
  return {
87389
87394
  name: this.serverName,
87390
87395
  command: "node",
@@ -87464,8 +87469,10 @@ var ToolProxyHost = class {
87464
87469
  text: typeof result === "string" ? result : JSON.stringify(result)
87465
87470
  }] };
87466
87471
  this.sendResponse(socket, createResponse(request.id, toolResult));
87467
- } else if (request.method === "getTools") this.sendResponse(socket, createResponse(request.id, this.getToolDefinitions()));
87468
- else this.sendResponse(socket, createErrorResponse(request.id, JsonRpcErrorCode.METHOD_NOT_FOUND, `Unknown method: ${request.method}`));
87472
+ } else if (request.method === "getTools") {
87473
+ const definitions = this.getToolDefinitions();
87474
+ this.sendResponse(socket, createResponse(request.id, definitions));
87475
+ } else this.sendResponse(socket, createErrorResponse(request.id, JsonRpcErrorCode.METHOD_NOT_FOUND, `Unknown method: ${request.method}`));
87469
87476
  } catch (error) {
87470
87477
  this.sendResponse(socket, createErrorResponse(request.id, JsonRpcErrorCode.INTERNAL_ERROR, error instanceof Error ? error.message : String(error)));
87471
87478
  }
@@ -87565,16 +87572,22 @@ function convertAiSdkMessagesToAcp(options, isFreshSession) {
87565
87572
  }
87566
87573
  return contentBlocks;
87567
87574
  }
87568
- function extractACPTools(tools) {
87575
+ function extractACPTools(tools, prepared = true) {
87569
87576
  const acpTools2 = [];
87570
87577
  if (!tools) return acpTools2;
87571
- for (const t of tools) if (t.type === "function") {
87578
+ const toolsArray = Array.isArray(tools) ? tools : Object.entries(tools).map(([name, tool2]) => ({
87579
+ type: "function",
87580
+ name,
87581
+ ...tool2
87582
+ }));
87583
+ for (const t of toolsArray) if (t.type === "function") {
87572
87584
  const toolInputSchema = t.inputSchema;
87573
87585
  if (hasRegisteredExecute(t.name) && toolInputSchema) {
87574
87586
  const execute = getExecuteByName(t.name);
87575
87587
  if (execute) acpTools2.push({
87576
87588
  ...t,
87577
87589
  name: t.name,
87590
+ inputSchema: prepared ? toolInputSchema : (0, ai.asSchema)(toolInputSchema).jsonSchema,
87578
87591
  execute
87579
87592
  });
87580
87593
  }
@@ -87607,12 +87620,10 @@ var ACPAISDKClient = class {
87607
87620
  optionId: params.options[0]?.optionId || "allow"
87608
87621
  } };
87609
87622
  }
87610
- writeTextFile(params) {
87611
- console.log("[acp-ai-provider] Write file request (not implemented):", params.path);
87623
+ writeTextFile(_params) {
87612
87624
  throw new Error("File operations not implemented in language model client");
87613
87625
  }
87614
- readTextFile(params) {
87615
- console.log("[acp-ai-provider] Read file request (not implemented):", params.path);
87626
+ readTextFile(_params) {
87616
87627
  throw new Error("File operations not implemented in language model client");
87617
87628
  }
87618
87629
  };
@@ -87658,6 +87669,7 @@ var ACPLanguageModel = class {
87658
87669
  */
87659
87670
  parseToolCall(update$1) {
87660
87671
  if (update$1.sessionUpdate !== "tool_call") throw new Error("Invalid update type for parseToolCall");
87672
+ console.log("Parsing tool call update:", JSON.stringify(update$1, null, 2));
87661
87673
  return {
87662
87674
  toolCallId: update$1.toolCallId,
87663
87675
  toolName: update$1.title || update$1.toolCallId,
@@ -87730,7 +87742,7 @@ var ACPLanguageModel = class {
87730
87742
  if (initResult.authMethods?.length ?? false) {
87731
87743
  if (!this.config.authMethodId || !validAuthMethods) console.log("[acp-ai-provider] Warning: No authMethodId specified in config, skipping authentication step. If this is not desired, please set one of the authMethodId in the ACPProviderSettings.", JSON.stringify(initResult.authMethods, null, 2));
87732
87744
  if (this.config.authMethodId && validAuthMethods) await this.connection.authenticate({ methodId: this.config.authMethodId ?? initResult.authMethods?.[0].id });
87733
- } else console.log(`[acp-ai-provider] No authentication methods required by the ACP agent, skipping authentication step.`);
87745
+ }
87734
87746
  }
87735
87747
  /**
87736
87748
  * Starts a new session or updates the existing one.
@@ -87738,19 +87750,19 @@ var ACPLanguageModel = class {
87738
87750
  */
87739
87751
  async startSession(acpTools2) {
87740
87752
  if (!this.connection) throw new Error("Not connected");
87741
- console.log(`[acp-ai-provider] startSession called with ${acpTools2?.length ?? 0} tools`);
87742
87753
  const mcpServers = [...this.config.session?.mcpServers ?? []];
87743
87754
  let toolsAdded = false;
87744
87755
  if (acpTools2 && acpTools2.length > 0 && !this.toolProxyHost) {
87745
- console.log("[acp-ai-provider] Setting up tool proxy for client-side tools...");
87756
+ console.log("[acp-ai-provider] Setting up tool proxy for client-side tools...", acpTools2.map((t) => t.name));
87746
87757
  this.toolProxyHost = new ToolProxyHost("acp-ai-sdk-tools");
87747
87758
  for (const t of acpTools2) this.toolProxyHost.registerTool(t.name, t);
87759
+ toolsAdded = true;
87760
+ }
87761
+ if (this.toolProxyHost) {
87748
87762
  const proxyConfig = await this.toolProxyHost.start();
87749
87763
  mcpServers.push(proxyConfig);
87750
- toolsAdded = true;
87751
87764
  }
87752
87765
  if (this.sessionId && toolsAdded) {
87753
- console.log("[acp-ai-provider] Updating session to include new tools...");
87754
87766
  this.sessionResponse = await this.connection.newSession({
87755
87767
  ...this.config.session,
87756
87768
  cwd: this.config.session?.cwd ?? node_process.default.cwd(),
@@ -87795,7 +87807,7 @@ var ACPLanguageModel = class {
87795
87807
  }
87796
87808
  async applySessionDelay() {
87797
87809
  if (this.config.sessionDelayMs) {
87798
- console.log(`[acp-ai-provider] Waiting for ${this.config.sessionDelayMs}ms after session setup...`);
87810
+ console.log(`[acp-ai-provider] Waiting ${this.config.sessionDelayMs}ms after session setup...`);
87799
87811
  await new Promise((resolve$1) => setTimeout(resolve$1, this.config.sessionDelayMs));
87800
87812
  }
87801
87813
  }
@@ -87830,14 +87842,9 @@ var ACPLanguageModel = class {
87830
87842
  *
87831
87843
  * @param acpTools - Optional list of tools to register during session initialization.
87832
87844
  */
87833
- async initSession(acpTools2) {
87834
- let toolsArray = [];
87835
- if (acpTools2) if (Array.isArray(acpTools2)) toolsArray = acpTools2;
87836
- else toolsArray = Object.entries(acpTools2).map(([name, tool2]) => ({
87837
- ...tool2,
87838
- name
87839
- }));
87840
- await this.ensureConnected(toolsArray);
87845
+ async initSession(tools) {
87846
+ const acpTools2 = extractACPTools(tools, false);
87847
+ await this.ensureConnected(acpTools2.length > 0 ? acpTools2 : void 0);
87841
87848
  return this.sessionResponse;
87842
87849
  }
87843
87850
  /**
@@ -87964,6 +87971,11 @@ var ACPLanguageModel = class {
87964
87971
  this.currentThinkingId = null;
87965
87972
  }
87966
87973
  const { toolCallId, toolName, toolInput } = this.parseToolCall(update$1);
87974
+ console.log(`Parsing tool call: ${JSON.stringify({
87975
+ toolCallId,
87976
+ toolName,
87977
+ toolInput
87978
+ }, null, 2)}`);
87967
87979
  const existingToolCall = this.toolCallsMap.get(toolCallId);
87968
87980
  const hasInput = toolInput && typeof toolInput === "object" && Object.keys(toolInput).length > 0;
87969
87981
  if (!existingToolCall) {
@@ -88004,7 +88016,6 @@ var ACPLanguageModel = class {
88004
88016
  break;
88005
88017
  }
88006
88018
  case "tool_call_update": {
88007
- console.log(`###[acp-ai-provider] tool_call_update received:`, JSON.stringify(update$1, null, 2));
88008
88019
  const { toolCallId, toolName, toolResult, isError, status } = this.parseToolResult(update$1);
88009
88020
  let toolInfo = this.toolCallsMap.get(toolCallId);
88010
88021
  if (status === "in_progress") {
@@ -88257,9 +88268,9 @@ var ACPProvider = class {
88257
88268
  * Initializes the session and returns session info (models, modes, meta).
88258
88269
  * Call this before prompting to discover available options.
88259
88270
  */
88260
- initSession(acpTools2) {
88271
+ initSession(tools) {
88261
88272
  if (!this.model) this.languageModel();
88262
- return this.model.initSession(acpTools2);
88273
+ return this.model.initSession(tools);
88263
88274
  }
88264
88275
  /**
88265
88276
  * Initializes the connection to the agent process without starting a session.
@@ -88298,6 +88309,22 @@ function createACPProvider(config) {
88298
88309
  //#endregion
88299
88310
  //#region src/middleware/acp-middleware.ts
88300
88311
  /**
88312
+ * Provider manager - stores one provider per agent config
88313
+ * Key: agentKey (command:args), Value: ProviderEntry
88314
+ */
88315
+ const providerManager = /* @__PURE__ */ new Map();
88316
+ /**
88317
+ * Session to provider mapping for quick lookup
88318
+ * Key: sessionId, Value: agentKey
88319
+ */
88320
+ const sessionToProvider = /* @__PURE__ */ new Map();
88321
+ /**
88322
+ * Generate a unique key for an agent configuration
88323
+ */
88324
+ function getAgentKey(command, args) {
88325
+ return `${command}:${(args || []).join(",")}`;
88326
+ }
88327
+ /**
88301
88328
  * Call MCP method via transport and wait for response
88302
88329
  */
88303
88330
  function callMcpMethodViaTransport(transport, method, params) {
@@ -88355,6 +88382,163 @@ function getActiveTransport() {
88355
88382
  return connectionManager.transports[sessionIds[0]];
88356
88383
  }
88357
88384
  function setupAcpMiddleware(middlewares, serverContext, acpOptions) {
88385
+ /**
88386
+ * Initialize a session for an agent
88387
+ * POST /api/acp/init-session
88388
+ * Body: { agent, envVars }
88389
+ * Returns: { sessionId }
88390
+ */
88391
+ middlewares.use("/api/acp/init-session", async (req, res) => {
88392
+ if (handleCors(res, req.method)) return;
88393
+ if (req.method !== "POST") {
88394
+ res.statusCode = 405;
88395
+ res.end("Method Not Allowed");
88396
+ return;
88397
+ }
88398
+ try {
88399
+ const body = await readBody(req);
88400
+ const { agent, envVars } = JSON.parse(body);
88401
+ const cwd$1 = process.cwd();
88402
+ const agentKey = getAgentKey(agent.command, agent.args);
88403
+ console.log(`[dev-inspector] [acp] Requesting session for agent: ${agent.name} (${agentKey})`);
88404
+ let providerEntry = providerManager.get(agentKey);
88405
+ let sessionId = "";
88406
+ if (providerEntry) {
88407
+ if (providerEntry.sessions.size > 0) {
88408
+ const firstSession = providerEntry.sessions.values().next().value;
88409
+ if (firstSession) {
88410
+ sessionId = firstSession.sessionId;
88411
+ console.log(`[dev-inspector] [acp] Reusing existing session: ${sessionId} for ${agent.name}`);
88412
+ }
88413
+ }
88414
+ if (!sessionId && providerEntry.initializationPromise) {
88415
+ console.log(`[dev-inspector] [acp] Joining pending initialization for ${agent.name}`);
88416
+ try {
88417
+ sessionId = await providerEntry.initializationPromise;
88418
+ } catch (e) {
88419
+ throw e;
88420
+ }
88421
+ }
88422
+ }
88423
+ if (!sessionId) {
88424
+ let provider;
88425
+ if (providerEntry) {
88426
+ console.log(`[dev-inspector] [acp] Reusing existing provider for ${agent.name}`);
88427
+ provider = providerEntry.provider;
88428
+ } else {
88429
+ console.log(`[dev-inspector] [acp] Creating new global provider for ${agent.name}`);
88430
+ provider = createACPProvider({
88431
+ command: agent.command,
88432
+ args: agent.args,
88433
+ env: {
88434
+ ...process.env,
88435
+ ...envVars
88436
+ },
88437
+ session: {
88438
+ cwd: cwd$1,
88439
+ mcpServers: []
88440
+ },
88441
+ authMethodId: agent.authMethodId,
88442
+ persistSession: true
88443
+ });
88444
+ providerEntry = {
88445
+ provider,
88446
+ agentKey,
88447
+ sessions: /* @__PURE__ */ new Map(),
88448
+ createdAt: Date.now(),
88449
+ initializationPromise: void 0
88450
+ };
88451
+ providerManager.set(agentKey, providerEntry);
88452
+ }
88453
+ console.log(`[dev-inspector] [acp] Spawning new process/session for ${agent.name}`);
88454
+ const initPromise = (async () => {
88455
+ const transport = getActiveTransport();
88456
+ let initialTools = {};
88457
+ if (transport) try {
88458
+ const rawTools = await loadMcpToolsV5(transport);
88459
+ initialTools = acpTools(rawTools);
88460
+ console.log(`[dev-inspector] [acp] Pre-loading ${Object.keys(rawTools).length} tools for session init`);
88461
+ } catch (e) {
88462
+ console.warn("[dev-inspector] [acp] Failed to pre-load tools:", e);
88463
+ }
88464
+ const sid = (await provider.initSession(initialTools)).sessionId || `session-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
88465
+ if (providerEntry) {
88466
+ providerEntry.sessions.set(sid, {
88467
+ sessionId: sid,
88468
+ createdAt: Date.now()
88469
+ });
88470
+ providerEntry.initializationPromise = void 0;
88471
+ }
88472
+ sessionToProvider.set(sid, agentKey);
88473
+ return sid;
88474
+ })();
88475
+ if (providerEntry) providerEntry.initializationPromise = initPromise;
88476
+ try {
88477
+ sessionId = await initPromise;
88478
+ console.log(`[dev-inspector] [acp] Session initialized: ${sessionId}`);
88479
+ } catch (error) {
88480
+ if (providerEntry) providerEntry.initializationPromise = void 0;
88481
+ throw error;
88482
+ }
88483
+ }
88484
+ res.setHeader("Content-Type", "application/json");
88485
+ res.end(JSON.stringify({ sessionId }));
88486
+ } catch (error) {
88487
+ console.error("ACP Init Session Error:", error);
88488
+ if (!res.headersSent) {
88489
+ res.statusCode = 500;
88490
+ res.end(JSON.stringify({ error: error instanceof Error ? error.message : "Internal Server Error" }));
88491
+ }
88492
+ }
88493
+ });
88494
+ /**
88495
+ * Cleanup a session
88496
+ * POST /api/acp/cleanup-session
88497
+ * Body: { sessionId }
88498
+ */
88499
+ middlewares.use("/api/acp/cleanup-session", async (req, res) => {
88500
+ if (handleCors(res, req.method)) return;
88501
+ if (req.method !== "POST") {
88502
+ res.statusCode = 405;
88503
+ res.end("Method Not Allowed");
88504
+ return;
88505
+ }
88506
+ try {
88507
+ const body = await readBody(req);
88508
+ const { sessionId } = JSON.parse(body);
88509
+ const agentKey = sessionToProvider.get(sessionId);
88510
+ if (agentKey) {
88511
+ const providerEntry = providerManager.get(agentKey);
88512
+ if (providerEntry) {
88513
+ console.log(`[dev-inspector] [acp] Cleaning up session: ${sessionId} (Provider sessions left: ${providerEntry.sessions.size - 1})`);
88514
+ providerEntry.sessions.delete(sessionId);
88515
+ if (providerEntry.sessions.size === 0) {
88516
+ console.log(`[dev-inspector] [acp] No active sessions for ${agentKey}, cleaning up provider`);
88517
+ try {
88518
+ providerEntry.provider.cleanup();
88519
+ } catch (e) {
88520
+ console.error("Error cleaning up provider:", e);
88521
+ }
88522
+ providerManager.delete(agentKey);
88523
+ }
88524
+ }
88525
+ sessionToProvider.delete(sessionId);
88526
+ }
88527
+ res.setHeader("Content-Type", "application/json");
88528
+ res.end(JSON.stringify({ success: true }));
88529
+ } catch (error) {
88530
+ console.error("ACP Cleanup Session Error:", error);
88531
+ if (!res.headersSent) {
88532
+ res.statusCode = 500;
88533
+ res.end(JSON.stringify({ error: "Internal Server Error" }));
88534
+ }
88535
+ }
88536
+ });
88537
+ /**
88538
+ * Chat endpoint
88539
+ * POST /api/acp/chat
88540
+ * Body: { messages, agent, envVars, sessionId? }
88541
+ */
88358
88542
  middlewares.use("/api/acp/chat", async (req, res) => {
88359
88543
  if (handleCors(res, req.method)) return;
88360
88544
  if (req.method !== "POST") {
@@ -88364,18 +88548,36 @@ function setupAcpMiddleware(middlewares, serverContext, acpOptions) {
88364
88548
  }
88365
88549
  try {
88366
88550
  const body = await readBody(req);
88367
- const { messages, agent, envVars } = JSON.parse(body);
88551
+ const { messages, agent, envVars, sessionId } = JSON.parse(body);
88368
88552
  const cwd$1 = process.cwd();
88369
- const provider = createACPProvider({
88370
- command: agent.command,
88371
- args: agent.args,
88372
- env: envVars,
88373
- session: {
88374
- cwd: cwd$1,
88375
- mcpServers: []
88376
- },
88377
- authMethodId: agent.authMethodId
88378
- });
88553
+ let provider;
88554
+ let shouldCleanupProvider = true;
88555
+ let existingProviderEntry;
88556
+ if (sessionId) {
88557
+ const agentKey = sessionToProvider.get(sessionId);
88558
+ if (agentKey) existingProviderEntry = providerManager.get(agentKey);
88559
+ }
88560
+ if (existingProviderEntry) {
88561
+ console.log(`[dev-inspector] [acp] Using existing global provider for session: ${sessionId}`);
88562
+ provider = existingProviderEntry.provider;
88563
+ shouldCleanupProvider = false;
88564
+ } else {
88565
+ console.log(`[dev-inspector] [acp] Creating new provider (no session found or provided)`);
88566
+ provider = createACPProvider({
88567
+ command: agent.command,
88568
+ args: agent.args,
88569
+ env: {
88570
+ ...process.env,
88571
+ ...envVars
88572
+ },
88573
+ session: {
88574
+ cwd: cwd$1,
88575
+ mcpServers: []
88576
+ },
88577
+ authMethodId: agent.authMethodId
88578
+ });
88579
+ await provider.initSession();
88580
+ }
88379
88581
  const transport = getActiveTransport();
88380
88582
  let mcpTools = {};
88381
88583
  if (transport) mcpTools = await loadMcpToolsV5(transport);
@@ -88391,7 +88593,7 @@ function setupAcpMiddleware(middlewares, serverContext, acpOptions) {
88391
88593
  req.on("close", () => {
88392
88594
  console.log("[dev-inspector] [acp] Client disconnected, aborting stream");
88393
88595
  abortController.abort();
88394
- provider.cleanup();
88596
+ if (shouldCleanupProvider) provider.cleanup();
88395
88597
  });
88396
88598
  const response = (0, ai.streamText)({
88397
88599
  model: provider.languageModel(model, mode),