#!/usr/bin/env node
import "./chunk-MCKGQKYU.js";

// index.ts
import { resolve as resolve3 } from "path";
import { fileURLToPath as fileURLToPath2 } from "url";

// tui.tsx
import { Command } from "commander";
import { render as render2 } from "ink";

// ../../core/mcp/node/config.ts
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import { dirname as dirname2, resolve as resolve2 } from "path";

// ../../core/storage/store-io.ts
import * as path from "path";
import * as fs from "fs/promises";
import { readFile, writeFile } from "atomically";

// ../../core/storage/store-serialize.ts
function serializeStore(data) {
  return JSON.stringify(data, null, 2);
}
function parseStore(raw) {
  return JSON.parse(raw);
}

// ../../core/storage/store-io.ts
function getDefaultStorageDir() {
  const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
  return path.join(homeDir, ".mcp-inspector", "storage");
}
function getDefaultMcpConfigPath() {
  const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
  return path.join(homeDir, ".mcp-inspector", "mcp.json");
}
function getStoreFilePath(storageDir, storeId) {
  return path.join(storageDir, `${storeId}.json`);
}
async function readStoreFile(filePath) {
  try {
    const data = await readFile(filePath, { encoding: "utf-8" });
    return data;
  } catch (error) {
    const err = error;
    if (err.code === "ENOENT") {
      return null;
    }
    throw error;
  }
}
var pendingWrites = /* @__PURE__ */ new Map();
async function writeStoreFile(filePath, data) {
  const key = path.resolve(filePath);
  const run = async () => {
    const dir = path.dirname(filePath);
    await fs.mkdir(dir, { recursive: true });
    await writeFile(filePath, data, {
      encoding: "utf-8",
      mode: 384
    });
  };
  const prior = pendingWrites.get(key);
  const tracked = (prior ?? Promise.resolve()).catch(() => {
  }).then(run);
  pendingWrites.set(key, tracked);
  try {
    await tracked;
  } finally {
    if (pendingWrites.get(key) === tracked) {
      pendingWrites.delete(key);
    }
  }
}
async function deleteStoreFile(filePath) {
  try {
    await fs.unlink(filePath);
  } catch (error) {
    const err = error;
    if (err.code !== "ENOENT") {
      throw error;
    }
  }
}

// ../../core/mcp/types.ts
function isTerminalStatus(status) {
  return status === "disconnected" || status === "error";
}
var INACTIVE_SUBSCRIPTION_STREAM_STATE = {
  active: false,
  status: "ended",
  honoredUris: []
};
var DEFAULT_TASK_TTL_MS = 6e4;
var DEFAULT_MAX_FETCH_REQUESTS = 1e3;
var DEFAULT_MODERN_LOG_LEVEL = "debug";
function resolveModernLogLevel(settings) {
  const level = settings?.modernLogLevel ?? DEFAULT_MODERN_LOG_LEVEL;
  return level === "off" ? void 0 : level;
}
var MODERN_LOG_LEVELS = [
  "off",
  "debug",
  "info",
  "notice",
  "warning",
  "error",
  "critical",
  "alert",
  "emergency"
];
function isModernLogLevel(value) {
  return typeof value === "string" && MODERN_LOG_LEVELS.includes(value);
}
var MODERN_PROTOCOL_VERSION = "2026-07-28";
function eraToVersionNegotiation(era) {
  switch (era) {
    case "auto":
      return { mode: "auto" };
    case "modern":
      return { mode: { pin: MODERN_PROTOCOL_VERSION } };
    case "legacy":
      return { mode: "legacy" };
  }
}

// ../../core/auth/secret-fields.ts
var SECRET_FIELD_OAUTH_CLIENT_SECRET = "oauth-client-secret";
var SECRET_FIELD_IDP_CLIENT_SECRET = "idp-client-secret";
var envSecretField = (envKey) => `env:${envKey}`;

// ../../core/json/jsonUtils.ts
function toRecord(value) {
  return value;
}
function convertParameterValue(value, schema) {
  if (!value) {
    return value;
  }
  if (schema.type === "number" || schema.type === "integer") {
    return Number(value);
  }
  if (schema.type === "boolean") {
    return value.toLowerCase() === "true";
  }
  if (schema.type === "object" || schema.type === "array") {
    try {
      return JSON.parse(value);
    } catch {
      return value;
    }
  }
  return value;
}
function convertToolParameters(tool, params) {
  const result = {};
  const properties = tool.inputSchema?.properties || {};
  for (const [key, value] of Object.entries(params)) {
    const paramSchema = properties[key];
    if (paramSchema) {
      result[key] = convertParameterValue(value, paramSchema);
    } else {
      result[key] = value;
    }
  }
  return result;
}
function convertPromptArguments(args) {
  const stringArgs = {};
  for (const [key, value] of Object.entries(args)) {
    if (typeof value === "string") {
      stringArgs[key] = value;
    } else if (value === null || value === void 0) {
      stringArgs[key] = String(value);
    } else {
      stringArgs[key] = JSON.stringify(value);
    }
  }
  return stringArgs;
}

// ../../core/mcp/serverList.ts
var VALID_SERVER_TYPES = /* @__PURE__ */ new Set([
  "stdio",
  "sse",
  "streamable-http"
]);
var VALID_PROTOCOL_ERAS = /* @__PURE__ */ new Set([
  "legacy",
  "auto",
  "modern"
]);
function isProtocolEra(value) {
  return typeof value === "string" && VALID_PROTOCOL_ERAS.has(value);
}
function normalizeServerType(config) {
  const type = config.type;
  let normalizedType;
  if (typeof type !== "string") {
    normalizedType = "stdio";
  } else if (type === "http") {
    normalizedType = "streamable-http";
  } else if (VALID_SERVER_TYPES.has(type)) {
    normalizedType = type;
  } else {
    normalizedType = "stdio";
  }
  return { ...config, type: normalizedType };
}
function cleanRoots(roots) {
  if (!Array.isArray(roots)) {
    console.warn("Ignoring `roots`: expected an array, got", typeof roots);
    return [];
  }
  return roots.filter((r) => {
    if (typeof r?.uri !== "string") {
      console.warn("Dropping root without a string `uri`:", r);
      return false;
    }
    return r.uri.trim() !== "";
  }).map((r) => {
    const rawName = r.name;
    if (rawName !== void 0 && typeof rawName !== "string") {
      console.warn("Dropping non-string `name` on root:", r);
    }
    const trimmedName = typeof rawName === "string" ? rawName.trim() : void 0;
    const { name: _name, ...rest } = r;
    return trimmedName ? { ...rest, name: trimmedName } : rest;
  });
}
function envRecordToPairs(env2) {
  return env2 ? Object.entries(env2).map(([key, value]) => ({ key, value })) : [];
}
function storedFieldsToInspectorSettings(stored) {
  const hasAny = stored.headers !== void 0 || stored.metadata !== void 0 || stored.connectionTimeout !== void 0 || stored.requestTimeout !== void 0 || stored.taskTtl !== void 0 || stored.autoRefreshOnListChanged !== void 0 || stored.paginatedLists !== void 0 || stored.advertisedExtensions !== void 0 || stored.maxFetchRequests !== void 0 || stored.oauth !== void 0 || stored.roots !== void 0 || stored.protocolEra !== void 0 || stored.modernLogLevel !== void 0 || stored.env !== void 0 || stored.cwd !== void 0;
  if (!hasAny) return void 0;
  const headersPairs = stored.headers ? Object.entries(stored.headers).map(([key, value]) => ({ key, value })) : [];
  const settings = {
    headers: headersPairs,
    env: envRecordToPairs(stored.env),
    metadata: stored.metadata ?? [],
    connectionTimeout: stored.connectionTimeout ?? 0,
    requestTimeout: stored.requestTimeout ?? 0,
    // Unlike the timeouts (0 = "SDK default"), task TTL has a concrete product
    // default so the form shows it and "Run as task" has a value to send.
    taskTtl: stored.taskTtl ?? DEFAULT_TASK_TTL_MS,
    autoRefreshOnListChanged: stored.autoRefreshOnListChanged ?? false,
    paginatedLists: stored.paginatedLists ?? false,
    // Concrete default like taskTtl (not a 0-sentinel): the form needs a value
    // to render and the log state needs one to size its buffer. An absent
    // on-disk field reads back as the default, which the write side then omits.
    maxFetchRequests: stored.maxFetchRequests ?? DEFAULT_MAX_FETCH_REQUESTS,
    // Defaults to an empty list so the form always has a concrete array to
    // render controlled rows from. An absent on-disk `roots` reads back as
    // `[]`, which `inspectorSettingsToStoredFields` then omits on write.
    roots: stored.roots ?? []
  };
  if (isProtocolEra(stored.protocolEra)) {
    settings.protocolEra = stored.protocolEra;
  }
  if (isModernLogLevel(stored.modernLogLevel)) {
    settings.modernLogLevel = stored.modernLogLevel;
  }
  if (stored.advertisedExtensions && Object.keys(stored.advertisedExtensions).length > 0) {
    settings.advertisedExtensions = { ...stored.advertisedExtensions };
  }
  if (stored.oauth?.clientId) settings.oauthClientId = stored.oauth.clientId;
  if (stored.oauth?.clientSecret)
    settings.oauthClientSecret = stored.oauth.clientSecret;
  if (stored.oauth?.scopes) settings.oauthScopes = stored.oauth.scopes;
  if (stored.oauth?.onInsufficientScope) {
    settings.oauthOnInsufficientScope = stored.oauth.onInsufficientScope;
  }
  if (stored.oauth?.enterpriseManaged === true) {
    settings.enterpriseManaged = true;
  }
  if (stored.cwd) settings.cwd = stored.cwd;
  return settings;
}
var INSPECTOR_FIELD_KEY_MAP = {
  headers: true,
  metadata: true,
  protocolEra: true,
  modernLogLevel: true,
  connectionTimeout: true,
  requestTimeout: true,
  taskTtl: true,
  autoRefreshOnListChanged: true,
  paginatedLists: true,
  advertisedExtensions: true,
  maxFetchRequests: true,
  oauth: true,
  roots: true
};
var INSPECTOR_FIELD_KEYS = new Set(
  Object.keys(INSPECTOR_FIELD_KEY_MAP)
);
function mcpConfigToServerEntries(config) {
  return Object.entries(config.mcpServers).map(([id, raw]) => {
    const inspectorFields = {};
    const sdkOnly = {};
    const inspectorRecord = toRecord(inspectorFields);
    for (const [k, v] of Object.entries(toRecord(raw))) {
      if (INSPECTOR_FIELD_KEYS.has(k)) {
        inspectorRecord[k] = v;
      } else {
        sdkOnly[k] = v;
      }
    }
    const normalizedConfig = normalizeServerType(
      sdkOnly
    );
    const entry = {
      id,
      name: id,
      config: normalizedConfig,
      connection: { status: "disconnected" }
    };
    const isStdio = normalizedConfig.type === "stdio" || normalizedConfig.type === void 0;
    const stdioConfig = isStdio ? normalizedConfig : void 0;
    const settings = storedFieldsToInspectorSettings({
      ...inspectorFields,
      env: stdioConfig?.env,
      cwd: stdioConfig?.cwd
    });
    if (settings !== void 0) entry.settings = settings;
    return entry;
  });
}
var isStdioStored = (stored) => stored.type === "stdio" || stored.type === void 0;
function mergeSecretsIntoStored(stored, secrets) {
  const out = { ...stored };
  const oauthSecret = secrets[SECRET_FIELD_OAUTH_CLIENT_SECRET];
  if (oauthSecret) {
    out.oauth = { ...out.oauth ?? {}, clientSecret: oauthSecret };
  }
  if (isStdioStored(out) && out.env) {
    const newEnv = { ...out.env };
    let mutated = false;
    for (const k of Object.keys(out.env)) {
      const val = secrets[envSecretField(k)];
      if (val !== void 0) {
        newEnv[k] = val;
        mutated = true;
      }
    }
    if (mutated) {
      out.env = newEnv;
    }
  }
  return out;
}
function expectedSecretFields(stored) {
  const fields = [];
  fields.push(SECRET_FIELD_OAUTH_CLIENT_SECRET);
  if (isStdioStored(stored) && stored.env) {
    for (const k of Object.keys(stored.env)) {
      fields.push(envSecretField(k));
    }
  }
  return fields;
}

// ../../core/mcp/node/config.ts
function parseKeyValuePair(value, previous = {}) {
  const parts = value.split("=");
  const key = parts[0] ?? "";
  const val = parts.slice(1).join("=");
  if (!key || val === void 0 || val === "") {
    throw new Error(
      `Invalid parameter format: ${value}. Use key=value format.`
    );
  }
  return { ...previous, [key]: val };
}
function parseHeaderPair(value, previous = {}) {
  const colonIndex = value.indexOf(":");
  if (colonIndex === -1) {
    throw new Error(
      `Invalid header format: ${value}. Use "HeaderName: Value" format.`
    );
  }
  const key = value.slice(0, colonIndex).trim();
  const val = value.slice(colonIndex + 1).trim();
  if (key === "" || val === "") {
    throw new Error(
      `Invalid header format: ${value}. Use "HeaderName: Value" format.`
    );
  }
  return { ...previous, [key]: val };
}
var EMPTY_CATALOG_CONTENT = `${JSON.stringify({ mcpServers: {} }, null, 2)}
`;
function seedEmptyCatalog(resolvedPath) {
  mkdirSync(dirname2(resolvedPath), { recursive: true });
  writeFileSync(resolvedPath, EMPTY_CATALOG_CONTENT, { mode: 384 });
}
function readServerListContent(configPath, writable) {
  const resolvedPath = resolve2(process.cwd(), configPath);
  if (!existsSync(resolvedPath)) {
    if (writable) {
      seedEmptyCatalog(resolvedPath);
      return EMPTY_CATALOG_CONTENT;
    }
    throw new Error(`Config file not found: ${resolvedPath}`);
  }
  return readFileSync(resolvedPath, "utf-8");
}
function loadMcpServersConfig(configPath, writable) {
  try {
    const configContent = readServerListContent(configPath, writable);
    const config = JSON.parse(configContent);
    if (!config.mcpServers) {
      throw new Error("Configuration file must contain an mcpServers element");
    }
    const normalizedServers = {};
    for (const [name, raw] of Object.entries(config.mcpServers)) {
      normalizedServers[name] = normalizeServerType(toRecord(raw));
    }
    return { ...config, mcpServers: normalizedServers };
  } catch (error) {
    if (error instanceof Error) {
      throw new Error(`Error loading configuration: ${error.message}`, {
        cause: error
      });
    }
    throw new Error("Error loading configuration: Unknown error", {
      cause: error
    });
  }
}
function loadServerFromConfig(configPath, serverName, writable) {
  const config = loadMcpServersConfig(configPath, writable);
  if (!config.mcpServers[serverName]) {
    const available = Object.keys(config.mcpServers).join(", ");
    throw new Error(
      `Server '${serverName}' not found in config file. Available servers: ${available}`
    );
  }
  return config.mcpServers[serverName];
}
function buildConfigFromOptions(options) {
  const target = options.target ?? [];
  const first = target[0];
  const rest = target.slice(1);
  const urlFromTarget = first && (first.startsWith("http://") || first.startsWith("https://")) ? first : null;
  const url = urlFromTarget ?? options.serverUrl ?? null;
  if (url) {
    if (rest.length > 0 && urlFromTarget) {
      throw new Error("Arguments cannot be passed to a URL-based MCP server.");
    }
    let transportType;
    const t = options.transport === "http" ? "streamable-http" : options.transport;
    if (t === "sse" || t === "streamable-http") {
      transportType = t;
    } else {
      const u = new URL(url);
      if (u.pathname.endsWith("/mcp")) {
        transportType = "streamable-http";
      } else if (u.pathname.endsWith("/sse")) {
        transportType = "sse";
      } else {
        throw new Error(
          `Transport type not specified and could not be determined from URL: ${url}.`
        );
      }
    }
    if (transportType === "sse") {
      const config3 = { type: "sse", url };
      return config3;
    }
    const config2 = { type: "streamable-http", url };
    return config2;
  }
  if (target.length === 0 || !first) {
    throw new Error(
      "Target is required. Specify a URL or a command to execute."
    );
  }
  if (options.transport && options.transport !== "stdio") {
    throw new Error("Only stdio transport can be used with local commands.");
  }
  const config = { type: "stdio", command: first };
  if (rest.length > 0) config.args = rest;
  if (options.env && Object.keys(options.env).length > 0)
    config.env = options.env;
  if (options.cwd?.trim()) config.cwd = options.cwd.trim();
  return config;
}
function applyOverrides(config, overrides) {
  if (config.type === "stdio") {
    const c = { ...config };
    if (overrides.env && Object.keys(overrides.env).length > 0) {
      c.env = { ...c.env ?? {}, ...overrides.env };
    }
    if (overrides.cwd?.trim()) c.cwd = overrides.cwd.trim();
    return c;
  }
  return config;
}
function hasAdHocServerOptions(options) {
  return options.target != null && options.target.length > 0 || Boolean(options.transport) || Boolean(options.serverUrl?.trim());
}
function resolveServerSource(options) {
  if (options.catalogPath?.trim()) {
    return { path: options.catalogPath, writable: true };
  }
  if (options.configPath?.trim()) {
    return { path: options.configPath, writable: false };
  }
  return null;
}
function serverSourceConflict(flags) {
  if (flags.hasCatalog && flags.hasConfig) {
    return "--catalog and --config are mutually exclusive. --catalog is the writable catalog; --config is a read-only session file.";
  }
  if (flags.hasCatalog && flags.hasAdHoc) {
    return "--catalog cannot be combined with an ad-hoc server URL/command.";
  }
  if (flags.hasConfig && flags.hasAdHoc) {
    return "--config cannot be combined with an ad-hoc server URL/command. --config selects a read-only session file.";
  }
  return null;
}
function readServerListFile(configPath, writable) {
  return loadMcpServersConfig(configPath, writable);
}
function withDefaultCatalogPath(options) {
  if (options.catalogPath?.trim() || options.configPath?.trim() || hasAdHocServerOptions(options)) {
    return options;
  }
  return { ...options, catalogPath: getDefaultMcpConfigPath() };
}
function resolveServerConfigs(options, mode) {
  const source = resolveServerSource(options);
  const hasAdHoc = hasAdHocServerOptions(options);
  if (mode === "single") {
    if (source && options.serverName) {
      const config = loadServerFromConfig(
        source.path,
        options.serverName,
        source.writable
      );
      return [
        applyOverrides(config, {
          env: options.env,
          cwd: options.cwd
        })
      ];
    }
    if (source && !options.serverName) {
      const mcpConfig = loadMcpServersConfig(source.path, source.writable);
      const servers = Object.keys(mcpConfig.mcpServers);
      if (servers.length === 0)
        throw new Error("No servers found in config file");
      if (servers.length > 1) {
        throw new Error(
          `Multiple servers found in config file. Please specify one with --server. Available servers: ${servers.join(", ")}`
        );
      }
      const serverName = servers[0];
      if (!serverName) throw new Error("No servers found in config file");
      const config = loadServerFromConfig(
        source.path,
        serverName,
        source.writable
      );
      return [
        applyOverrides(config, {
          env: options.env,
          cwd: options.cwd
        })
      ];
    }
    return [buildConfigFromOptions(options)];
  }
  if (mode === "multi") {
    if (source && hasAdHoc) {
      throw new Error(
        "In multi-server mode with a config file, do not pass --transport, --server-url, or positional command/URL. Use only --config with optional -e, --cwd."
      );
    }
    if (source) {
      const mcpConfig = loadMcpServersConfig(source.path, source.writable);
      const configs = Object.values(mcpConfig.mcpServers).map(
        (c) => applyOverrides({ ...c }, {
          env: options.env,
          cwd: options.cwd
        })
      );
      return configs;
    }
    return [buildConfigFromOptions(options)];
  }
  return [];
}

// ../../core/auth/node/secret-store.ts
var SERVICE_NAME = "mcp-inspector";
var keyringLoad;
var KeyringModuleShapeError = class extends Error {
  constructor(detail, options) {
    super(`@napi-rs/keyring loaded but ${detail}`, options);
    this.name = "KeyringModuleShapeError";
  }
};
var checkKeyringShape = (mod) => {
  try {
    if (typeof mod.AsyncEntry === "function" && typeof mod.findCredentialsAsync === "function") {
      return { ok: true, mod };
    }
    return {
      ok: false,
      err: new KeyringModuleShapeError(
        "did not expose AsyncEntry / findCredentialsAsync"
      )
    };
  } catch (err) {
    return {
      ok: false,
      err: new KeyringModuleShapeError(
        `its exports could not be read: ${err instanceof Error ? err.message : String(err)}`,
        { cause: err }
      )
    };
  }
};
var loadKeyring = () => {
  keyringLoad ??= import("@napi-rs/keyring").then(
    checkKeyringShape,
    (err) => ({ ok: false, err })
  );
  return keyringLoad;
};
function parseAccount(account) {
  const idx = account.indexOf(":");
  if (idx <= 0 || idx === account.length - 1) return null;
  return {
    serverId: account.slice(0, idx),
    field: account.slice(idx + 1)
  };
}
var buildAccount = (serverId, field) => `${serverId}:${field}`;
var KeychainUnavailableError = class extends Error {
  constructor(cause) {
    const message = cause instanceof Error ? cause.message : String(cause);
    super(
      `OS keychain is not available. ${hintFor(cause, message)} Underlying error: ${message}`
    );
    this.name = "KeychainUnavailableError";
  }
};
var hintFor = (cause, message) => {
  if (cause instanceof KeyringModuleShapeError) {
    return `The @napi-rs/keyring package loaded but does not expose the API this build expects \u2014 most likely a version or packaging mismatch; reinstall the Inspector, and report this if it persists.`;
  }
  if (message.includes("Cannot find native binding")) {
    return `The @napi-rs/keyring platform package for this OS is missing or unavailable \u2014 reinstall the Inspector (for npx, clear the npx cache under your npm cache directory first).`;
  }
  return `On Linux, install libsecret / gnome-keyring.`;
};
var KeyringSecretStore = class {
  async get(serverId, field) {
    try {
      const keyring = await loadKeyring();
      if (!keyring.ok) return null;
      const entry = new keyring.mod.AsyncEntry(
        SERVICE_NAME,
        buildAccount(serverId, field)
      );
      const v = await entry.getPassword();
      return v ?? null;
    } catch {
      return null;
    }
  }
  async set(serverId, field, value) {
    try {
      const keyring = await loadKeyring();
      if (!keyring.ok) throw new KeychainUnavailableError(keyring.err);
      const entry = new keyring.mod.AsyncEntry(
        SERVICE_NAME,
        buildAccount(serverId, field)
      );
      await entry.setPassword(value);
    } catch (err) {
      if (err instanceof KeychainUnavailableError) throw err;
      throw new KeychainUnavailableError(err);
    }
  }
  async delete(serverId, field) {
    try {
      const keyring = await loadKeyring();
      if (!keyring.ok) return;
      const entry = new keyring.mod.AsyncEntry(
        SERVICE_NAME,
        buildAccount(serverId, field)
      );
      await entry.deleteCredential();
    } catch {
    }
  }
  async deleteAllForServer(serverId) {
    let creds;
    try {
      const keyring = await loadKeyring();
      if (!keyring.ok) return;
      creds = await keyring.mod.findCredentialsAsync(SERVICE_NAME);
    } catch {
      return;
    }
    const prefix = `${serverId}:`;
    for (const c of creds) {
      if (!c.account.startsWith(prefix)) continue;
      const parsed = parseAccount(c.account);
      if (!parsed || parsed.serverId !== serverId) continue;
      await this.delete(serverId, parsed.field);
    }
  }
};

// ../../core/mcp/node/server-secrets.ts
async function rehydrateMcpConfigFromKeychain(config, secretStore) {
  const out = { mcpServers: {} };
  await Promise.all(
    Object.entries(config.mcpServers).map(async ([id, stored]) => {
      const fields = expectedSecretFields(stored);
      const secrets = {};
      await Promise.all(
        fields.map(async (field) => {
          const value = await secretStore.get(id, field);
          if (value !== null) {
            secrets[field] = value;
          }
        })
      );
      out.mcpServers[id] = mergeSecretsIntoStored(stored, secrets);
    })
  );
  return out;
}

// ../../core/mcp/node/servers.ts
function headersToServerSettings(headers) {
  if (!headers || Object.keys(headers).length === 0) {
    return void 0;
  }
  return {
    headers: Object.entries(headers).map(([key, value]) => ({ key, value })),
    env: [],
    metadata: [],
    connectionTimeout: 0,
    requestTimeout: 0,
    taskTtl: DEFAULT_TASK_TTL_MS,
    maxFetchRequests: DEFAULT_MAX_FETCH_REQUESTS,
    autoRefreshOnListChanged: false,
    paginatedLists: false,
    roots: []
  };
}
function mergeSettings(base, headers) {
  const fromHeaders = headersToServerSettings(headers);
  if (!fromHeaders) return base;
  if (!base) return fromHeaders;
  return { ...base, headers: fromHeaders.headers };
}
async function loadServerEntries(serverOptions) {
  serverOptions = withDefaultCatalogPath(serverOptions);
  const conflict = serverSourceConflict({
    hasCatalog: Boolean(serverOptions.catalogPath?.trim()),
    hasConfig: Boolean(serverOptions.configPath?.trim()),
    hasAdHoc: hasAdHocServerOptions(serverOptions)
  });
  if (conflict) {
    throw new Error(conflict);
  }
  const source = resolveServerSource(serverOptions);
  if (source) {
    let config = readServerListFile(source.path, source.writable);
    const secretStore = serverOptions.secretStore ?? new KeyringSecretStore();
    config = await rehydrateMcpConfigFromKeychain(config, secretStore);
    const entries = mcpConfigToServerEntries(config);
    const result = {};
    for (const entry of entries) {
      result[entry.name] = {
        config: applyOverrides(entry.config, {
          env: serverOptions.env,
          cwd: serverOptions.cwd
        }),
        // Deliberate broadcast: a single `--header` set is merged into EVERY
        // server in the catalog/config (fine for the common single-server case;
        // for multi-server files, prefer per-server headers in the file itself).
        settings: mergeSettings(entry.settings, serverOptions.headers)
      };
    }
    return result;
  }
  const configs = resolveServerConfigs(serverOptions, "multi");
  if (configs.length === 0) {
    throw new Error(
      "At least one server is required. Use --catalog/--config <path> or an ad-hoc target (command/URL)."
    );
  }
  return {
    default: {
      config: configs[0],
      settings: headersToServerSettings(serverOptions.headers)
    }
  };
}

// ../../core/mcp/config.ts
function getServerType(config) {
  if (!("type" in config) || config.type === void 0) {
    return "stdio";
  }
  const type = config.type;
  if (type === "stdio") {
    return "stdio";
  }
  if (type === "sse") {
    return "sse";
  }
  if (type === "streamable-http") {
    return "streamable-http";
  }
  throw new Error(
    `Invalid server type: ${type}. Valid types are: stdio, sse, streamable-http`
  );
}

// ../../core/mcp/node/transport.ts
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { SSEClientTransport } from "@modelcontextprotocol/client";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/client";

// ../../core/mcp/fetchTracking.ts
var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
  "authorization",
  "cookie",
  "set-cookie",
  "proxy-authorization",
  "x-api-key",
  // The inspector backend's own bearer (createRemoteFetch stamps this on every
  // proxied request); same exposure as Authorization.
  "x-mcp-remote-auth"
]);
var REDACTED_HEADER_VALUE = "[REDACTED]";
var SENSITIVE_BODY_FIELDS = /* @__PURE__ */ new Set([
  "client_secret",
  "code",
  "refresh_token",
  "access_token",
  "id_token",
  "code_verifier",
  "client_assertion",
  "assertion",
  "password",
  "token"
]);
var REDACTED_VALUE = "[REDACTED]";
function isSensitiveField(name) {
  return SENSITIVE_BODY_FIELDS.has(name.toLowerCase());
}
function redactSensitiveHeaders(headers) {
  const out = {};
  for (const [key, value] of Object.entries(headers)) {
    out[key] = SENSITIVE_HEADERS.has(key.toLowerCase()) ? REDACTED_HEADER_VALUE : value;
  }
  return out;
}
function redactUrlQuery(url) {
  const queryStart = url.indexOf("?");
  if (queryStart === -1) return url;
  const base = url.slice(0, queryStart);
  const afterQuery = url.slice(queryStart + 1);
  const hashStart = afterQuery.indexOf("#");
  const query = hashStart === -1 ? afterQuery : afterQuery.slice(0, hashStart);
  const fragment = hashStart === -1 ? "" : afterQuery.slice(hashStart);
  try {
    const params = new URLSearchParams(query);
    let changed = false;
    for (const key of new Set(params.keys())) {
      if (isSensitiveField(key)) {
        changed = true;
        params.set(key, REDACTED_VALUE);
      }
    }
    if (!changed) return url;
    return `${base}?${params.toString()}${fragment}`;
  } catch {
    return url;
  }
}
function redactJsonValue(value) {
  if (Array.isArray(value)) {
    return value.map(redactJsonValue);
  }
  if (value !== null && typeof value === "object") {
    const out = {};
    for (const [key, val] of Object.entries(value)) {
      out[key] = isSensitiveField(key) && typeof val === "string" ? REDACTED_VALUE : redactJsonValue(val);
    }
    return out;
  }
  return value;
}
function redactBody(body, contentType) {
  if (!body) return body;
  const type = (contentType ?? "").toLowerCase();
  if (type.includes("application/x-www-form-urlencoded")) {
    try {
      const params = new URLSearchParams(body);
      let changed = false;
      for (const key of new Set(params.keys())) {
        if (isSensitiveField(key)) {
          changed = true;
          params.set(key, REDACTED_VALUE);
        }
      }
      return changed ? params.toString() : body;
    } catch {
      return body;
    }
  }
  try {
    const parsed = JSON.parse(body);
    if (parsed !== null && typeof parsed === "object") {
      return JSON.stringify(redactJsonValue(parsed));
    }
  } catch {
  }
  return body;
}
function findHeader(headers, name) {
  const target = name.toLowerCase();
  for (const [key, value] of Object.entries(headers)) {
    if (key.toLowerCase() === target) return value;
  }
  return void 0;
}
function isLongLivedStreamResponse(method, contentType) {
  if (method !== "GET") return false;
  if (!contentType) return false;
  return contentType.includes("text/event-stream") || contentType.includes("application/x-ndjson");
}
function createFetchTracker(baseFetch, callbacks) {
  return async (input, init) => {
    const startTime = Date.now();
    const timestamp = /* @__PURE__ */ new Date();
    const id = `${timestamp.getTime()}-${Math.random().toString(36).slice(2, 11)}`;
    const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
    const method = init?.method || "GET";
    const rawRequestHeaders = {};
    if (input instanceof Request) {
      input.headers.forEach((value, key) => {
        rawRequestHeaders[key] = value;
      });
    }
    if (init?.headers) {
      const headers = new Headers(init.headers);
      headers.forEach((value, key) => {
        rawRequestHeaders[key] = value;
      });
    }
    const requestHeaders = redactSensitiveHeaders(rawRequestHeaders);
    const requestContentType = findHeader(rawRequestHeaders, "content-type");
    const redactedUrl = redactUrlQuery(url);
    let requestBody;
    if (init?.body) {
      if (typeof init.body === "string") {
        requestBody = init.body;
      } else {
        try {
          requestBody = String(init.body);
        } catch {
          requestBody = void 0;
        }
      }
    } else if (input instanceof Request && input.body) {
      try {
        const cloned = input.clone();
        requestBody = await cloned.text();
      } catch {
        requestBody = void 0;
      }
    }
    const redactedRequestBody = redactBody(requestBody, requestContentType);
    let response;
    let error;
    try {
      response = await baseFetch(input, init);
    } catch (err) {
      error = err instanceof Error ? err.message : String(err);
      const entry2 = {
        id,
        timestamp,
        method,
        url: redactedUrl,
        requestHeaders,
        requestBody: redactedRequestBody,
        error,
        duration: Date.now() - startTime
      };
      callbacks.trackRequest?.(entry2);
      throw err;
    }
    const responseStatus = response.status;
    const responseStatusText = response.statusText;
    const rawResponseHeaders = {};
    response.headers.forEach((value, key) => {
      rawResponseHeaders[key] = value;
    });
    const responseHeaders = redactSensitiveHeaders(rawResponseHeaders);
    const isLongLivedStream = isLongLivedStreamResponse(
      method,
      response.headers.get("content-type")
    );
    const duration = Date.now() - startTime;
    const entry = {
      id,
      timestamp,
      method,
      url: redactedUrl,
      requestHeaders,
      requestBody: redactedRequestBody,
      responseStatus,
      responseStatusText,
      responseHeaders,
      responseBody: void 0,
      duration
    };
    callbacks.trackRequest?.(entry);
    if (!isLongLivedStream && response.body && !response.bodyUsed) {
      const responseContentType = response.headers.get("content-type");
      try {
        const cloned = response.clone();
        cloned.text().then((body) => {
          callbacks.updateResponseBody?.(
            id,
            redactBody(body, responseContentType) ?? body
          );
        }).catch(() => {
        });
      } catch {
      }
    }
    return response;
  };
}

// ../../core/auth/utils.ts
import { UnauthorizedError } from "@modelcontextprotocol/client";
import { ZodError } from "zod";
function parseHttpUrl(value, label) {
  const trimmed = value.trim();
  try {
    return new URL(trimmed);
  } catch (err) {
    const detail = err instanceof Error ? err.message : String(err);
    throw new Error(`Invalid ${label}: "${trimmed}" (${detail})`, {
      cause: err
    });
  }
}
var parseOAuthCallbackParams = (location) => {
  const params = new URLSearchParams(location);
  const code = params.get("code");
  if (code) {
    const iss = params.get("iss");
    return iss === null ? { successful: true, code } : { successful: true, code, iss };
  }
  const error = params.get("error");
  const error_description = params.get("error_description");
  const error_uri = params.get("error_uri");
  if (error) {
    return { successful: false, error, error_description, error_uri };
  }
  return {
    successful: false,
    error: "invalid_request",
    error_description: "Missing code or error in response",
    error_uri: null
  };
};
var generateOAuthState = () => {
  if (typeof crypto === "undefined" || !crypto.getRandomValues) {
    throw new Error(
      "crypto.getRandomValues is not available; refusing to generate an OAuth state with a non-cryptographic RNG."
    );
  }
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join(
    ""
  );
};
var parseOAuthState = (state) => {
  if (!state || typeof state !== "string") return null;
  if (/^[a-f0-9]{64}$/i.test(state)) {
    return { authId: state };
  }
  return null;
};
var generateOAuthErrorDescription = (params) => {
  const error = params.error;
  const errorDescription = params.error_description;
  const errorUri = params.error_uri;
  return [
    `Error: ${error}.`,
    errorDescription ? `Details: ${errorDescription}.` : "",
    errorUri ? `More info: ${errorUri}.` : ""
  ].filter(Boolean).join("\n");
};
function isUnauthorizedError(err) {
  return isUnauthorizedErrorDeep(err, /* @__PURE__ */ new Set());
}
function isUnauthorizedErrorDeep(err, seen) {
  if (err == null) return false;
  if (typeof err !== "object") {
    return /\bfailed\b[^\n]*\(401\)/i.test(String(err));
  }
  if (seen.has(err)) return false;
  seen.add(err);
  if (UnauthorizedError.isInstance(err)) return true;
  const status = err.status;
  const code = err.code;
  if (status === 401 || code === 401) return true;
  if (err instanceof Error && /\bfailed\b[^\n]*\(401\)/i.test(err.message)) {
    return true;
  }
  if ("cause" in err && isUnauthorizedErrorDeep(err.cause, seen)) {
    return true;
  }
  const data = err.data;
  if (data !== null && typeof data === "object" && "cause" in data && isUnauthorizedErrorDeep(data.cause, seen)) {
    return true;
  }
  return false;
}

// ../../core/auth/challenge.ts
var EMA_STEP_UP_PENDING_URL = new URL("mcp-inspector:ema-step-up");
var AuthChallengeError = class extends Error {
  authChallenge;
  status;
  constructor(authChallenge, status, message) {
    super(message ?? `Auth challenge: ${authChallenge.reason}`);
    this.name = "AuthChallengeError";
    this.authChallenge = authChallenge;
    this.status = status;
  }
};
var AuthRecoveryRequiredError = class extends Error {
  authorizationUrl;
  authChallenge;
  /** EMA insufficient_scope awaiting user confirmation in Inspector (no redirect yet). */
  emaStepUpConfirm;
  constructor(authorizationUrl, authChallenge, options) {
    super("Interactive auth recovery required");
    this.name = "AuthRecoveryRequiredError";
    this.authorizationUrl = authorizationUrl;
    this.authChallenge = authChallenge;
    this.emaStepUpConfirm = options?.emaStepUpConfirm;
  }
};
function isConnectAuthRecoveryError(err) {
  if (err instanceof AuthRecoveryRequiredError) return true;
  return isUnauthorizedError(err);
}
function findNestedAuthError(err) {
  return findNestedAuthErrorDeep(err, /* @__PURE__ */ new Set());
}
function findNestedAuthErrorDeep(err, seen) {
  if (err === null || typeof err !== "object" || seen.has(err)) {
    return void 0;
  }
  seen.add(err);
  if (err instanceof AuthRecoveryRequiredError || err instanceof AuthChallengeError) {
    return err;
  }
  const nested = findNestedAuthErrorDeep(
    err.cause,
    seen
  );
  if (nested) {
    return nested;
  }
  const data = err.data;
  if (data !== null && typeof data === "object") {
    return findNestedAuthErrorDeep(data.cause, seen);
  }
  return void 0;
}
function parseWwwAuthenticateBearer(header) {
  const match = header.match(/Bearer\s+(.+)/i);
  if (!match) {
    return {};
  }
  const params = {};
  const paramRegex = /([\w-]+)(?:="([^"]*)"|=([^\s,]+))?/g;
  let m;
  while ((m = paramRegex.exec(match[1])) !== null) {
    const value = m[2] ?? m[3];
    if (value !== void 0) {
      params[m[1].toLowerCase()] = value;
    }
  }
  return {
    error: params.error,
    scope: params.scope,
    resourceMetadata: params.resource_metadata,
    errorDescription: params.error_description
  };
}
function parseScopeString(scope) {
  if (!scope?.trim()) {
    return [];
  }
  return scope.trim().split(/\s+/).filter(Boolean);
}
function unionAuthorizationScopes(previousScope, requiredScopes) {
  const seen = /* @__PURE__ */ new Set();
  const result = [];
  for (const scope of [
    ...parseScopeString(previousScope),
    ...requiredScopes.filter(Boolean)
  ]) {
    if (!seen.has(scope)) {
      seen.add(scope);
      result.push(scope);
    }
  }
  return result;
}
function reasonFromHttpResponse(status, bearer) {
  if (status === 403) {
    if (bearer.error === "insufficient_scope") {
      return "insufficient_scope";
    }
    return "unauthorized";
  }
  if (bearer.error === "insufficient_scope") {
    return "insufficient_scope";
  }
  if (bearer.error === "invalid_token") {
    return "invalid_token";
  }
  if (status === 401) {
    return "token_expired";
  }
  return "unauthorized";
}
function parseAuthChallengeFromResponse(response, context) {
  const status = response.status;
  if (status !== 401 && status !== 403) {
    return void 0;
  }
  const wwwAuthenticate = response.headers.get("WWW-Authenticate") ?? void 0;
  const bearer = wwwAuthenticate ? parseWwwAuthenticateBearer(wwwAuthenticate) : {};
  const requiredScopes = parseScopeString(bearer.scope);
  return {
    reason: reasonFromHttpResponse(status, bearer),
    ...requiredScopes.length > 0 ? { requiredScopes } : {},
    ...bearer.errorDescription ? { message: bearer.errorDescription } : {},
    ...context ? { context } : {},
    raw: {
      httpStatus: status,
      ...wwwAuthenticate ? { wwwAuthenticate } : {}
    }
  };
}
function parseAuthChallengeFromError(err, context) {
  if (err instanceof AuthChallengeError) {
    return err.authChallenge;
  }
  if (typeof err !== "object" || err === null) {
    return void 0;
  }
  const authChallenge = err.authChallenge;
  if (authChallenge?.reason) {
    return {
      ...authChallenge,
      ...context ? {
        context: {
          ...authChallenge.context,
          ...context
        }
      } : {}
    };
  }
  const status = err.status ?? err.code;
  if (status !== 401 && status !== 403) {
    return void 0;
  }
  const wwwAuthenticate = authChallenge?.raw?.wwwAuthenticate ?? err.wwwAuthenticate ?? err.headers?.get?.("WWW-Authenticate") ?? void 0;
  if (!wwwAuthenticate?.length) {
    return void 0;
  }
  const bearer = parseWwwAuthenticateBearer(wwwAuthenticate);
  const requiredScopes = parseScopeString(bearer.scope);
  return {
    reason: reasonFromHttpResponse(status, bearer),
    ...requiredScopes.length > 0 ? { requiredScopes } : {},
    ...context ? { context } : {},
    raw: {
      httpStatus: status,
      wwwAuthenticate
    }
  };
}
function isAuthChallengeError(err) {
  if (err instanceof AuthChallengeError) {
    return true;
  }
  if (typeof err !== "object" || err === null) {
    return false;
  }
  const authChallenge = err.authChallenge;
  if (authChallenge?.reason) {
    return true;
  }
  const status = err.status ?? err.code;
  if (status !== 401 && status !== 403) {
    return false;
  }
  const wwwAuthenticate = authChallenge?.raw?.wwwAuthenticate ?? err.wwwAuthenticate ?? err.headers?.get?.("WWW-Authenticate") ?? void 0;
  return wwwAuthenticate !== void 0 && wwwAuthenticate.length > 0;
}

// ../../core/mcp/node/authChallengeFetch.ts
function createAuthChallengeInterceptFetch(baseFetch) {
  return async (input, init) => {
    const response = await baseFetch(input, init);
    if (response.status !== 401 && response.status !== 403) {
      return response;
    }
    const challenge = parseAuthChallengeFromResponse(response);
    if (!challenge) {
      return response;
    }
    await response.body?.cancel().catch(() => {
    });
    throw new AuthChallengeError(
      challenge,
      response.status,
      `MCP auth challenge (${response.status})`
    );
  };
}

// ../../core/mcp/node/transport.ts
var PROXY_ENV_VARS = [
  "HTTPS_PROXY",
  "https_proxy",
  "HTTP_PROXY",
  "http_proxy"
];
function readProxyEnv() {
  for (const name of PROXY_ENV_VARS) {
    const value = process.env[name];
    if (value && value.trim() !== "") return value;
  }
  return void 0;
}
function withProxyDispatcher(baseFetch) {
  if (readProxyEnv() === void 0) return baseFetch;
  let dispatcherPromise;
  const getDispatcher = () => {
    dispatcherPromise ??= import("./undici-HXPKCIY3.js").then(
      ({ EnvHttpProxyAgent }) => new EnvHttpProxyAgent(),
      (cause) => {
        throw new Error(
          "HTTPS_PROXY / HTTP_PROXY is set but the `undici` package is not available. Install it (it ships with the CLI client) or unset the proxy env var.",
          { cause }
        );
      }
    );
    return dispatcherPromise;
  };
  const proxiedFetch = async (input, init) => {
    const dispatcher = await getDispatcher();
    return baseFetch(input, { ...init, dispatcher });
  };
  return proxiedFetch;
}
function headersFromSettings(settings) {
  if (!settings || settings.headers.length === 0) return void 0;
  const out = {};
  for (const { key, value } of settings.headers) {
    if (key.trim() === "") continue;
    out[key] = value;
  }
  return Object.keys(out).length > 0 ? out : void 0;
}
function createTransportNode(config, options = {}) {
  const serverType = getServerType(config);
  const {
    fetchFn: optionsFetchFn,
    onStderr,
    pipeStderr = false,
    onFetchRequest,
    onFetchResponseBody,
    authProvider,
    settings,
    interceptAuthChallenges = false
  } = options;
  const baseFetch = withProxyDispatcher(optionsFetchFn ?? globalThis.fetch);
  const fetchWithOptionalAuthIntercept = interceptAuthChallenges ? createAuthChallengeInterceptFetch(baseFetch) : baseFetch;
  if (serverType === "stdio") {
    const stdioConfig = config;
    const transport = new StdioClientTransport({
      command: stdioConfig.command,
      args: stdioConfig.args || [],
      env: stdioConfig.env,
      cwd: stdioConfig.cwd,
      stderr: pipeStderr ? "pipe" : void 0
    });
    if (pipeStderr && transport.stderr && onStderr) {
      transport.stderr.on("data", (data) => {
        const logEntry = data.toString().trim();
        if (logEntry) {
          onStderr({
            timestamp: /* @__PURE__ */ new Date(),
            message: logEntry
          });
        }
      });
    }
    return { transport };
  } else if (serverType === "sse") {
    const sseConfig = config;
    const url = new URL(sseConfig.url);
    const sseFetch = sseConfig.eventSourceInit?.fetch || fetchWithOptionalAuthIntercept;
    const trackedFetch = onFetchRequest ? createFetchTracker(sseFetch, {
      trackRequest: onFetchRequest,
      updateResponseBody: onFetchResponseBody
    }) : sseFetch;
    const headers = headersFromSettings(settings);
    const eventSourceInit = {
      ...sseConfig.eventSourceInit,
      ...headers && { headers },
      fetch: trackedFetch
    };
    const requestInit = {
      ...sseConfig.requestInit,
      ...headers && { headers }
    };
    const postFetch = onFetchRequest ? createFetchTracker(fetchWithOptionalAuthIntercept, {
      trackRequest: onFetchRequest,
      updateResponseBody: onFetchResponseBody
    }) : fetchWithOptionalAuthIntercept;
    const transport = new SSEClientTransport(url, {
      authProvider,
      eventSourceInit,
      requestInit,
      fetch: postFetch
    });
    return { transport };
  } else {
    const httpConfig = config;
    const url = new URL(httpConfig.url);
    const headers = headersFromSettings(settings);
    const requestInit = {
      ...httpConfig.requestInit,
      ...headers && { headers }
    };
    const transportFetch = onFetchRequest ? createFetchTracker(fetchWithOptionalAuthIntercept, {
      trackRequest: onFetchRequest,
      updateResponseBody: onFetchResponseBody
    }) : fetchWithOptionalAuthIntercept;
    const transport = new StreamableHTTPClientTransport(url, {
      authProvider,
      requestInit,
      fetch: transportFetch,
      // SEP-2350: how the transport reacts to a `403 insufficient_scope`
      // challenge. Defaults to the SDK's `reauthorize` when unset.
      ...settings?.oauthOnInsufficientScope && {
        onInsufficientScope: settings.oauthOnInsufficientScope
      }
    });
    return { transport };
  }
}

// ../../core/client/config-parse.ts
import { z } from "zod";
function isAbsoluteHttpUrl(value) {
  let url;
  try {
    url = new URL(value.trim());
  } catch {
    return false;
  }
  if (url.protocol !== "http:" && url.protocol !== "https:") return false;
  return isRealHost(url.hostname);
}
function isRealHost(hostname) {
  if (hostname === "localhost") return true;
  if (hostname.startsWith("[")) return hostname.endsWith("]");
  const labels = hostname.split(".");
  return labels.length >= 2 && labels.every((label) => label !== "");
}
var HttpUrlStringSchema = z.string().min(1).superRefine((val, ctx) => {
  const trimmed = val.trim();
  if (!isAbsoluteHttpUrl(trimmed)) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: `Invalid URL: "${trimmed}" \u2014 must be an http(s) URL (e.g. https://idp.example.com)`
    });
  }
});
var CIMD_METADATA_URL_INVALID_ERROR = "Must be a valid URL, like https://example.com/oauth/client.json";
var CIMD_METADATA_URL_HTTPS_ERROR = "CIMD client metadata URL must use HTTPS";
var CIMD_METADATA_URL_PATH_ERROR = "Must include a path (not the site root), like https://example.com/oauth/client.json";
function getCimdClientMetadataUrlError(value) {
  const trimmed = value.trim();
  if (trimmed === "") return void 0;
  if (!isAbsoluteHttpUrl(trimmed)) {
    return CIMD_METADATA_URL_INVALID_ERROR;
  }
  try {
    const url = new URL(trimmed);
    if (url.protocol !== "https:") {
      return CIMD_METADATA_URL_HTTPS_ERROR;
    }
    if (url.pathname === "/" || url.pathname === "") {
      return CIMD_METADATA_URL_PATH_ERROR;
    }
  } catch {
    return CIMD_METADATA_URL_INVALID_ERROR;
  }
  return void 0;
}
function refineCimdMetadataUrl(val, ctx, required) {
  const trimmed = val.trim();
  if (trimmed === "") {
    if (required) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        message: "CIMD client metadata URL is required when CIMD is enabled"
      });
    }
    return;
  }
  const error = getCimdClientMetadataUrlError(trimmed);
  if (error) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: error
    });
  }
}
var CimdConfigSchema = z.object({
  enabled: z.boolean(),
  clientMetadataUrl: z.string()
}).superRefine((data, ctx) => {
  refineCimdMetadataUrl(data.clientMetadataUrl, ctx, data.enabled === true);
});
var EnterpriseManagedAuthIdpConfigSchema = z.object({
  issuer: HttpUrlStringSchema,
  clientId: z.string().min(1),
  clientSecret: z.string().optional()
});
var ClientConfigSchema = z.object({
  enterpriseManagedAuth: z.object({
    enabled: z.boolean().optional(),
    idp: EnterpriseManagedAuthIdpConfigSchema
  }).optional(),
  cimd: CimdConfigSchema.optional()
});
function parseClientConfig(raw) {
  return ClientConfigSchema.parse(raw);
}

// ../../core/client/secrets.ts
var CLIENT_KEYCHAIN_ID = "client";
function hasClientPlaintextSecret(config) {
  const secret = config.enterpriseManagedAuth?.idp?.clientSecret;
  return typeof secret === "string" && secret.length > 0;
}
function extractSecretsFromClientConfig(config) {
  const secrets = {};
  const ema = config.enterpriseManagedAuth;
  if (!ema?.idp?.clientSecret) {
    return { stripped: config, secrets };
  }
  const { clientSecret, ...idpRest } = ema.idp;
  secrets[SECRET_FIELD_IDP_CLIENT_SECRET] = clientSecret;
  return {
    stripped: {
      ...config,
      enterpriseManagedAuth: {
        ...ema,
        idp: idpRest
      }
    },
    secrets
  };
}
function mergeSecretsIntoClientConfig(config, secrets) {
  const idpSecret = secrets[SECRET_FIELD_IDP_CLIENT_SECRET];
  const ema = config.enterpriseManagedAuth;
  if (!idpSecret || !ema?.idp) {
    return config;
  }
  return {
    ...config,
    enterpriseManagedAuth: {
      ...ema,
      idp: {
        ...ema.idp,
        clientSecret: idpSecret
      }
    }
  };
}

// ../../core/client/node-persistence.ts
async function readIdpSecretFromKeychain(secretStore) {
  const secret = await secretStore.get(
    CLIENT_KEYCHAIN_ID,
    SECRET_FIELD_IDP_CLIENT_SECRET
  );
  if (!secret) return {};
  return { [SECRET_FIELD_IDP_CLIENT_SECRET]: secret };
}
async function migrateClientPlaintextSecret(filePath, config, secretStore) {
  const { stripped, secrets } = extractSecretsFromClientConfig(config);
  const value = secrets[SECRET_FIELD_IDP_CLIENT_SECRET];
  if (!value) return config;
  try {
    const existing = await secretStore.get(
      CLIENT_KEYCHAIN_ID,
      SECRET_FIELD_IDP_CLIENT_SECRET
    );
    if (existing === null) {
      await secretStore.set(
        CLIENT_KEYCHAIN_ID,
        SECRET_FIELD_IDP_CLIENT_SECRET,
        value
      );
    }
    await writeStoreFile(filePath, serializeStore(stripped));
    return stripped;
  } catch (err) {
    if (err instanceof KeychainUnavailableError) {
      return config;
    }
    throw err;
  }
}
async function readClientConfigStore(filePath, secretStore) {
  const raw = await readStoreFile(filePath);
  if (raw === null) {
    return {};
  }
  let config = parseClientConfig(parseStore(raw));
  if (hasClientPlaintextSecret(config)) {
    config = await migrateClientPlaintextSecret(filePath, config, secretStore);
  }
  const secrets = await readIdpSecretFromKeychain(secretStore);
  return mergeSecretsIntoClientConfig(config, secrets);
}

// ../../core/client/config.ts
function getClientConfigFilePath(customPath) {
  return customPath ?? getStoreFilePath(getDefaultStorageDir(), "client");
}
async function loadClientConfig(options) {
  const filePath = getClientConfigFilePath(options?.filePath);
  if (options?.secretStore) {
    return readClientConfigStore(filePath, options.secretStore);
  }
  const raw = await readStoreFile(filePath);
  if (raw === null) {
    return {};
  }
  return parseClientConfig(parseStore(raw));
}

// ../../core/client/types.ts
function isEnterpriseManagedAuthEnabled(config) {
  const ema = config.enterpriseManagedAuth;
  if (!ema?.idp) return false;
  return ema.enabled !== false;
}
function getActiveEnterpriseManagedAuthIdp(config) {
  if (!isEnterpriseManagedAuthEnabled(config)) return void 0;
  return config.enterpriseManagedAuth.idp;
}
function isCimdEnabled(config) {
  return config.cimd?.enabled === true;
}
function getActiveCimdClientMetadataUrl(config) {
  if (!isCimdEnabled(config)) return void 0;
  const url = config.cimd?.clientMetadataUrl?.trim();
  return url || void 0;
}

// ../../core/client/runner.ts
async function loadRunnerClientConfig(options) {
  const customPath = options?.clientConfigPath?.trim() || process.env.MCP_CLIENT_CONFIG_PATH?.trim() || void 0;
  const secretStore = options?.secretStore ?? new KeyringSecretStore();
  return loadClientConfig({ filePath: customPath, secretStore });
}
function isOAuthCapableServerConfig(config) {
  if (!config) return false;
  return config.type === "sse" || config.type === "streamable-http";
}
function buildRunnerClientAuthOptions(clientConfig, savedSettings, cliOverrides) {
  const activeIdp = getActiveEnterpriseManagedAuthIdp(clientConfig);
  const activeCimdUrl = getActiveCimdClientMetadataUrl(clientConfig);
  const oauthFromServer = savedSettings && (savedSettings.oauthClientId || savedSettings.oauthClientSecret || savedSettings.oauthScopes || savedSettings.enterpriseManaged) ? {
    ...savedSettings.oauthClientId && {
      clientId: savedSettings.oauthClientId
    },
    ...savedSettings.oauthClientSecret && {
      clientSecret: savedSettings.oauthClientSecret
    },
    ...savedSettings.oauthScopes && {
      scope: savedSettings.oauthScopes
    },
    ...savedSettings.enterpriseManaged && {
      enterpriseManaged: true
    }
  } : void 0;
  const clientMetadataUrl = cliOverrides?.clientMetadataUrl?.trim() || activeCimdUrl;
  const oauthFromCli = cliOverrides?.clientId || cliOverrides?.clientSecret || clientMetadataUrl ? {
    ...cliOverrides?.clientId && { clientId: cliOverrides.clientId },
    ...cliOverrides?.clientSecret && {
      clientSecret: cliOverrides.clientSecret
    },
    ...clientMetadataUrl && { clientMetadataUrl }
  } : void 0;
  const oauth = oauthFromServer || oauthFromCli ? {
    ...oauthFromServer ?? {},
    ...oauthFromCli ?? {}
  } : void 0;
  return {
    ...oauth && { oauth },
    ...activeIdp && {
      enterpriseManagedAuth: { idp: activeIdp }
    },
    ...clientConfig.enterpriseManagedAuth && {
      installEnterpriseManagedAuth: clientConfig.enterpriseManagedAuth
    },
    ...oauth && { directAuthRecovery: true }
  };
}

// ../../core/node/hostUrl.ts
import { isIPv6 } from "net";
function stripBrackets(host) {
  return host.replace(/^\[(.*)\]$/, "$1");
}
function formatHostForUrl(host) {
  const h = host.trim();
  const bare = stripBrackets(h).split("%")[0];
  return isIPv6(bare) ? `[${bare}]` : h;
}
function unmapIpv4MappedHost(host) {
  const m = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i.exec(
    stripBrackets(host)
  );
  if (!m) return host;
  const hi = parseInt(m[1], 16);
  const lo = parseInt(m[2], 16);
  return `${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`;
}
function canonicalUrlHost(host) {
  const formatted = formatHostForUrl(host.trim().toLowerCase());
  try {
    return unmapIpv4MappedHost(new URL(`http://${formatted}`).hostname);
  } catch {
    return formatted;
  }
}
function isLoopbackHost(host) {
  const h = canonicalUrlHost(host).replace(/\.$/, "");
  return h === "localhost" || h === "[::1]" || /^127(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/.test(h);
}

// ../../core/auth/node/runner-oauth-callback.ts
var RUNNER_OAUTH_CALLBACK_DEFAULT_HOSTNAME = "127.0.0.1";
var RUNNER_OAUTH_CALLBACK_DEFAULT_PORT = 6276;
var RUNNER_OAUTH_CALLBACK_PATH = "/oauth/callback";
var DEFAULT_RUNNER_OAUTH_CALLBACK_URL = `http://${RUNNER_OAUTH_CALLBACK_DEFAULT_HOSTNAME}:${RUNNER_OAUTH_CALLBACK_DEFAULT_PORT}${RUNNER_OAUTH_CALLBACK_PATH}`;
function parseRunnerOAuthCallbackUrl(cliCallbackUrl) {
  const raw = cliCallbackUrl?.trim() || process.env.MCP_OAUTH_CALLBACK_URL?.trim() || "";
  if (!raw) {
    return {
      hostname: RUNNER_OAUTH_CALLBACK_DEFAULT_HOSTNAME,
      port: RUNNER_OAUTH_CALLBACK_DEFAULT_PORT,
      pathname: RUNNER_OAUTH_CALLBACK_PATH
    };
  }
  let url;
  try {
    url = new URL(raw);
  } catch (err) {
    const reason = err?.message ?? String(err);
    throw new Error(`Invalid OAuth callback URL: ${reason}`, { cause: err });
  }
  if (url.protocol !== "http:") {
    throw new Error("OAuth callback URL must use http scheme");
  }
  const hostname = url.hostname;
  if (!hostname) {
    throw new Error("OAuth callback URL must include a hostname");
  }
  if (!isLoopbackHost(hostname)) {
    throw new Error(
      `OAuth callback URL must bind a loopback host (localhost / 127.0.0.1 / [::1]), not "${hostname}": the callback listener receives the OAuth authorization code over plaintext http, so a network-reachable host risks credential interception. Use 127.0.0.1 (and a port-forward if the browser is elsewhere).`
    );
  }
  const pathname = url.pathname || "/";
  let port;
  if (url.port === "") {
    port = 80;
  } else {
    port = Number(url.port);
    if (!Number.isFinite(port) || !Number.isInteger(port) || port < 0 || port > 65535) {
      throw new Error("OAuth callback URL port must be between 0 and 65535");
    }
  }
  return { hostname: stripBrackets(hostname), port, pathname };
}
function formatRunnerOAuthRedirectUrl(config) {
  return `http://${formatHostForUrl(config.hostname)}:${config.port}${config.pathname}`;
}

// src/App.tsx
import {
  useState as useState23,
  useMemo as useMemo3,
  useEffect as useEffect20,
  useCallback as useCallback11,
  useRef as useRef13
} from "react";
import { Box as Box27, Text as Text24, useInput as useInput21, useApp } from "ink";

// ../../core/mcp/inspectorClient.ts
import { Client } from "@modelcontextprotocol/client";

// ../../core/json/xMcpHeader.ts
var X_MCP_HEADER_KEY = "x-mcp-header";
var MCP_PARAM_HEADER_PREFIX = "Mcp-Param-";
var RFC9110_TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
var PERMITTED_X_MCP_HEADER_TYPES = /* @__PURE__ */ new Set([
  "string",
  "integer",
  "boolean",
  "number"
]);
var NON_REACHABLE_SUBSCHEMA_KEYWORDS = [
  "items",
  "prefixItems",
  "contains",
  "additionalProperties",
  "unevaluatedProperties",
  "unevaluatedItems",
  "propertyNames",
  "patternProperties",
  "dependentSchemas",
  "oneOf",
  "anyOf",
  "allOf",
  "not",
  "if",
  "then",
  "else",
  "$defs",
  "definitions"
];
var OBJECT_VALUED_SUBSCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
  "patternProperties",
  "dependentSchemas",
  "$defs",
  "definitions"
]);
function pathName(path3) {
  return path3.length === 0 ? "<root>" : path3.join(".");
}
function isRecord(node) {
  return node !== null && typeof node === "object";
}
function scanXMcpHeaderDeclarations(inputSchema) {
  const declarations = [];
  const seenLower = /* @__PURE__ */ new Map();
  const visit = (node, path3, reachable) => {
    if (!isRecord(node)) return void 0;
    const schema = node;
    if (X_MCP_HEADER_KEY in schema) {
      if (!reachable || path3.length === 0) {
        return `${pathName(path3)}: x-mcp-header is only permitted on properties statically reachable via a chain of 'properties' keys (not under items, additionalProperties, oneOf/anyOf/allOf/not, if/then/else, or $ref)`;
      }
      const raw = schema[X_MCP_HEADER_KEY];
      if (typeof raw !== "string" || raw.length === 0) {
        return `${pathName(path3)}: x-mcp-header MUST be a non-empty string`;
      }
      if (!RFC9110_TOKEN.test(raw)) {
        return `${pathName(path3)}: x-mcp-header '${raw}' is not a valid RFC 9110 token (no spaces, control characters or HTTP delimiters)`;
      }
      const type = typeof schema.type === "string" ? schema.type : void 0;
      if (type === void 0 || !PERMITTED_X_MCP_HEADER_TYPES.has(type)) {
        return `${pathName(path3)}: x-mcp-header is only permitted on primitive-typed properties (string, integer, boolean); got ${type ?? "<none>"}`;
      }
      const lower = raw.toLowerCase();
      const prior = seenLower.get(lower);
      if (prior !== void 0) {
        return `x-mcp-header '${raw}' is not case-insensitively unique (also declared as '${prior}')`;
      }
      seenLower.set(lower, raw);
      declarations.push({ path: path3, headerName: raw, type });
    }
    const properties = schema.properties;
    if (isRecord(properties)) {
      for (const [key, child] of Object.entries(properties)) {
        const fault2 = visit(child, [...path3, key], reachable);
        if (fault2 !== void 0) return fault2;
      }
    }
    for (const k of NON_REACHABLE_SUBSCHEMA_KEYWORDS) {
      const sub = schema[k];
      if (sub === void 0) continue;
      const branches = Array.isArray(sub) ? sub : isRecord(sub) && OBJECT_VALUED_SUBSCHEMA_KEYWORDS.has(k) ? Object.values(sub) : [sub];
      for (const branch of branches) {
        const fault2 = visit(branch, [...path3, `<${k}>`], false);
        if (fault2 !== void 0) return fault2;
      }
    }
    return void 0;
  };
  const fault = visit(inputSchema, [], true);
  return fault === void 0 ? { valid: true, declarations } : { valid: false, reason: fault };
}
var BASE64_SENTINEL_PREFIX = "=?base64?";
var BASE64_SENTINEL_SUFFIX = "?=";
function mcpParamPrimitiveToString(value) {
  if (typeof value === "string") return value;
  if (typeof value === "boolean") return value ? "true" : "false";
  if (typeof value === "number") {
    if (!Number.isFinite(value)) return void 0;
    if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
      return void 0;
    }
    return String(value);
  }
  return void 0;
}
function needsBase64(s) {
  if (s.length === 0) return true;
  if (s.startsWith(BASE64_SENTINEL_PREFIX) && s.endsWith(BASE64_SENTINEL_SUFFIX)) {
    return true;
  }
  if (s !== s.trim()) return true;
  for (let i = 0; i < s.length; i++) {
    const c = s.codePointAt(i);
    if (c === 9 || c >= 32 && c <= 126) continue;
    return true;
  }
  return false;
}
function utf8ToBase64(s) {
  const bytes = new TextEncoder().encode(s);
  let bin = "";
  for (const b of bytes) bin += String.fromCodePoint(b);
  return btoa(bin);
}
function encodeMcpParamValue(value) {
  return needsBase64(value) ? `${BASE64_SENTINEL_PREFIX}${utf8ToBase64(value)}${BASE64_SENTINEL_SUFFIX}` : value;
}
function valueAtPath(root, path3) {
  let node = root;
  for (const key of path3) {
    if (node === null || typeof node !== "object") return void 0;
    node = node[key];
  }
  return node;
}
function buildMcpParamHeaders(declarations, args) {
  const out = {};
  for (const decl of declarations) {
    const raw = valueAtPath(args, decl.path);
    if (raw === void 0 || raw === null) continue;
    const stringValue = mcpParamPrimitiveToString(raw);
    if (stringValue === void 0) continue;
    out[`${MCP_PARAM_HEADER_PREFIX}${decl.headerName}`] = encodeMcpParamValue(stringValue);
  }
  return out;
}
function mcpParamHeadersForTool(tool, args) {
  const scan = scanXMcpHeaderDeclarations(tool.inputSchema);
  if (!scan.valid || scan.declarations.length === 0) return {};
  return buildMcpParamHeaders(scan.declarations, args);
}

// ../../core/mcp/messageTrackingTransport.ts
var MessageTrackingTransport = class {
  baseTransport;
  callbacks;
  negotiatedProtocolVersion;
  hooks;
  constructor(baseTransport, callbacks, hooks = {}) {
    this.baseTransport = baseTransport;
    this.callbacks = callbacks;
    this.hooks = hooks;
  }
  async start() {
    return this.baseTransport.start();
  }
  async send(message, options) {
    if ("id" in message && message.id !== null && message.id !== void 0) {
      if ("result" in message || "error" in message) {
        this.callbacks.trackResponse?.(
          message,
          "client"
        );
      } else if ("method" in message) {
        this.callbacks.trackRequest?.(message, "client");
      }
    } else if ("method" in message) {
      this.callbacks.trackNotification?.(
        message,
        "client"
      );
    }
    return this.baseTransport.send(message, options);
  }
  async close() {
    return this.baseTransport.close();
  }
  get onclose() {
    return this.baseTransport.onclose;
  }
  set onclose(handler) {
    this.baseTransport.onclose = handler;
  }
  get onerror() {
    return this.baseTransport.onerror;
  }
  set onerror(handler) {
    this.baseTransport.onerror = handler;
  }
  get onmessage() {
    return this.baseTransport.onmessage;
  }
  set onmessage(handler) {
    if (handler) {
      this.baseTransport.onmessage = (message, extra) => {
        if ("id" in message && message.id !== null && message.id !== void 0) {
          if ("result" in message || "error" in message) {
            this.callbacks.trackResponse?.(
              message,
              "server"
            );
            if (this.hooks.consumeIncomingResponse?.(
              message
            )) {
              return;
            }
            if (this.hooks.rewriteIncomingResult && "result" in message) {
              const rewritten = this.hooks.rewriteIncomingResult(
                message
              );
              if (rewritten !== message) {
                handler(rewritten, extra);
                return;
              }
            }
          } else if ("method" in message) {
            this.callbacks.trackRequest?.(message, "server");
          }
        } else if ("method" in message) {
          this.callbacks.trackNotification?.(
            message,
            "server"
          );
        }
        handler(message, extra);
      };
    } else {
      this.baseTransport.onmessage = void 0;
    }
  }
  get sessionId() {
    return this.baseTransport.sessionId;
  }
  // Implemented as a concrete method (rather than delegating the base
  // transport's optional `setProtocolVersion`) so the SDK Client always
  // invokes it after the initialize handshake — including for stdio, whose
  // base transport has no `setProtocolVersion`. We capture the negotiated
  // version for the UI here, then forward to the base transport when it
  // cares (HTTP transports stamp it into subsequent request headers).
  setProtocolVersion(version) {
    this.negotiatedProtocolVersion = version;
    this.baseTransport.setProtocolVersion?.(version);
  }
  /** MCP protocol version negotiated during initialize, once connected. */
  get protocolVersion() {
    return this.negotiatedProtocolVersion;
  }
};

// ../../core/mcp/inspectorClient.ts
import { ProtocolError as ProtocolError2, ProtocolErrorCode as ProtocolErrorCode2 } from "@modelcontextprotocol/client";
import {
  isInputRequiredResult,
  withInputRequired,
  LOG_LEVEL_META_KEY,
  CLIENT_CAPABILITIES_META_KEY,
  CLIENT_INFO_META_KEY,
  PROTOCOL_VERSION_META_KEY,
  RELATED_TASK_META_KEY as RELATED_TASK_META_KEY3
} from "@modelcontextprotocol/client";

// ../../core/mcp/modernTaskSchemas.ts
import { z as z2 } from "zod/v4";
var TASKS_EXTENSION_KEY = "io.modelcontextprotocol/tasks";
var MODERN_PROTOCOL_VERSION2 = "2026-07-28";
var TASKS_EXTENSION_CLIENT_CAPABILITY = {
  extensions: { [TASKS_EXTENSION_KEY]: {} }
};
var MODERN_TASK_HANDLE_META = "io.modelcontextprotocol/inspector/modernTaskHandle";
function isModernCreateTaskResult(result) {
  return typeof result === "object" && result !== null && result.resultType === "task" && typeof result.taskId === "string";
}
var ModernTaskStatusSchema = z2.enum([
  "working",
  "input_required",
  "completed",
  "failed",
  "cancelled"
]);
var ModernDetailedTaskSchema = z2.looseObject({
  taskId: z2.string(),
  status: ModernTaskStatusSchema,
  statusMessage: z2.string().optional(),
  createdAt: z2.string(),
  lastUpdatedAt: z2.string(),
  ttlMs: z2.number().nullable().optional(),
  pollIntervalMs: z2.number().optional(),
  /** Present on `completed`: the original request's result (e.g. CallToolResult). */
  result: z2.record(z2.string(), z2.unknown()).optional(),
  /** Present on `failed`: the JSON-RPC error that ended the task. */
  error: z2.record(z2.string(), z2.unknown()).optional(),
  /** Present on `input_required`: embedded server→client requests, keyed by id. */
  inputRequests: z2.record(z2.string(), z2.unknown()).optional()
});
var ModernGetTaskResultSchema = ModernDetailedTaskSchema;
var ModernUpdateTaskResultSchema = z2.looseObject({});
var ModernCancelTaskResultSchema = z2.looseObject({});
function normalizeModernTask(modern) {
  const { ttlMs, pollIntervalMs, ...rest } = modern;
  const normalized = { ...rest };
  normalized.ttl = ttlMs ?? null;
  if (pollIntervalMs != null) normalized.pollInterval = pollIntervalMs;
  return normalized;
}
function readInputRequests(modern) {
  return modern.inputRequests;
}

// ../../core/mcp/extensions.ts
var EMA_EXTENSION_KEY = "io.modelcontextprotocol/enterprise-managed-authorization";
var UI_EXTENSION_KEY = "io.modelcontextprotocol/ui";
var MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
var ADVERTISABLE_EXTENSIONS = [
  {
    key: TASKS_EXTENSION_KEY,
    label: "Tasks (io.modelcontextprotocol/tasks)",
    // The modern Tasks extension (SEP-2663). Advertised by default so the SDK
    // stamps it into every modern request envelope — the per-request
    // declaration a server requires before it may return a `CreateTaskResult`
    // (server-directed task creation). Harmless on legacy (extensions ignored).
    defaultAdvertised: true
  },
  {
    key: UI_EXTENSION_KEY,
    label: "MCP Apps UI (io.modelcontextprotocol/ui)",
    // The MCP Apps UI extension. The Inspector always renders App tools, so it
    // advertises this by default with the App resource MIME type it supports —
    // a conforming server checks the `mimeTypes` before serving a UI resource.
    defaultAdvertised: true,
    advertisement: { mimeTypes: [MCP_APP_MIME_TYPE] }
  }
];
function buildClientExtensions(input) {
  const map = {};
  for (const ext of ADVERTISABLE_EXTENSIONS) {
    const advertised = input.advertised?.[ext.key] ?? ext.defaultAdvertised;
    if (advertised) {
      map[ext.key] = ext.advertisement ? structuredClone(ext.advertisement) : {};
    }
  }
  if (input.enterpriseManaged) {
    map[EMA_EXTENSION_KEY] = {};
  }
  return map;
}

// ../../core/mcp/inspectorClient.ts
import {
  EmptyResultSchema,
  CallToolResultSchema,
  GetPromptResultSchema,
  ReadResourceResultSchema,
  ListTasksRequestSchema,
  GetTaskRequestSchema,
  GetTaskPayloadRequestSchema,
  CancelTaskRequestSchema,
  TaskStatusNotificationSchema,
  CreateTaskResultSchema,
  GetTaskResultSchema,
  CancelTaskResultSchema,
  ListTasksResultSchema,
  ListToolsResultSchema,
  ListResourcesResultSchema,
  ListResourceTemplatesResultSchema,
  ListPromptsResultSchema
} from "@modelcontextprotocol/core";
import { AjvJsonSchemaValidator } from "@modelcontextprotocol/client/validators/ajv";

// ../../core/mcp/toolOutputValidation.ts
function validateToolOutput(provider, tool, result) {
  if (!tool.outputSchema) return void 0;
  const structured = result.structuredContent;
  if (structured == null) {
    return result.isError ? void 0 : `Tool "${tool.name}" declares an output schema but returned no structured content`;
  }
  try {
    const validate = provider.getValidator(tool.outputSchema);
    const validation = validate(structured);
    return validation.valid ? void 0 : validation.errorMessage;
  } catch {
    return void 0;
  }
}

// ../../core/mcp/taskNotificationSchemas.ts
import { NotificationSchema } from "@modelcontextprotocol/core";
import { z as z3 } from "zod/v4";
var TasksListChangedNotificationSchema = NotificationSchema.extend({
  method: z3.literal("notifications/tasks/list_changed"),
  params: z3.record(z3.string(), z3.unknown()).optional()
});

// ../../core/mcp/inspectorClient.ts
import { UriTemplate } from "@modelcontextprotocol/client";

// ../../core/mcp/typedEventTarget.ts
var TypedEventGeneric = class extends CustomEvent {
  constructor(type, detail) {
    super(type, { detail });
  }
};
var TypedEventTarget = class extends EventTarget {
  dispatchTypedEvent(type, ...args) {
    const detail = args[0] ?? void 0;
    this.dispatchEvent(new TypedEventGeneric(type, detail));
  }
  addEventListener(type, listener, options) {
    super.addEventListener(
      type,
      listener,
      options
    );
  }
  removeEventListener(type, listener, options) {
    super.removeEventListener(
      type,
      listener,
      options
    );
  }
};

// ../../core/mcp/inspectorClientEventTarget.ts
var InspectorClientEventTarget = class extends TypedEventTarget {
};

// ../../core/mcp/samplingCreateMessage.ts
import { RELATED_TASK_META_KEY } from "@modelcontextprotocol/client";
var SamplingCreateMessage = class {
  id;
  timestamp;
  request;
  taskId;
  /**
   * How this request reached the Inspector — a legacy server→client request or
   * a modern MRTR `input_required` round. Drives era-accurate copy in the
   * pending-request UI. Defaults to `"server-request"` so existing call sites
   * (and stories) keep the legacy semantics unchanged.
   */
  origin;
  resolvePromise;
  rejectPromise;
  onRemove;
  constructor(request, resolve4, reject, onRemove, origin = "server-request") {
    this.onRemove = onRemove;
    this.id = `sampling-${crypto.randomUUID()}`;
    this.timestamp = /* @__PURE__ */ new Date();
    this.request = request;
    const relatedTask = request.params?._meta?.[RELATED_TASK_META_KEY];
    this.taskId = relatedTask?.taskId;
    this.origin = origin;
    this.resolvePromise = resolve4;
    this.rejectPromise = reject;
  }
  /**
   * Respond to the sampling request with a result
   */
  async respond(result) {
    if (!this.resolvePromise) {
      throw new Error("Request already resolved or rejected");
    }
    this.resolvePromise(result);
    this.resolvePromise = void 0;
    this.rejectPromise = void 0;
    this.remove();
  }
  /**
   * Reject the sampling request with an error
   */
  async reject(error) {
    if (!this.rejectPromise) {
      throw new Error("Request already resolved or rejected");
    }
    this.rejectPromise(error);
    this.resolvePromise = void 0;
    this.rejectPromise = void 0;
    this.remove();
  }
  /**
   * Settle a still-pending sample as cancelled, without removing it from the
   * queue. Called from `InspectorClient`'s `clearPendingPeerRequests()`, which
   * serves every route that drops the queue wholesale — each route out of a
   * connection, and the top of `connect()` as a backstop for the one route in
   * that settles nothing. The category, not a count: that set has grown.
   *
   * Unlike an elicitation there is no internal awaiter to unblock, but on the
   * plain server-request shape the *server* is one: we accepted its
   * `sampling/createMessage`, so dropping the request without settling means no
   * response frame is ever written and it waits forever. That is reachable when
   * the transport outlives the failed attempt — `connect()` keeps it when an
   * auth provider holds it open — so settle rather than discard. Rejecting is
   * the right settle: it is what the UI's decline path sends, and the SDK turns
   * it into a JSON-RPC error response. No-op once already resolved.
   *
   * A *task-augmented* sample sits in the same queue but already answered the
   * wire request synchronously with a `CreateTaskResult`, so nothing is waiting
   * on a frame; settling there is about not leaving the task in
   * `input_required` limbo. Its reject reaches the receiver task's payload
   * promise, which `InspectorClient` marks handled at creation for exactly this
   * reason.
   *
   * Deliberately does not call `onRemove`, for the same reason as
   * `ElicitationCreateMessage.cancel()`: the caller iterates the queue and
   * clears it itself, so removing here would splice mid-iteration — skipping
   * every other entry and leaving those requests unanswered.
   */
  cancel() {
    if (this.rejectPromise) {
      this.rejectPromise(new Error("Connection torn down"));
    }
    this.resolvePromise = void 0;
    this.rejectPromise = void 0;
  }
  /**
   * Remove this pending sample from the list
   */
  remove() {
    this.onRemove(this.id);
  }
};

// ../../core/mcp/elicitationCreateMessage.ts
import { RELATED_TASK_META_KEY as RELATED_TASK_META_KEY2 } from "@modelcontextprotocol/client";
var ElicitationCreateMessage = class {
  id;
  timestamp;
  request;
  taskId;
  /**
   * How this request reached the Inspector — a legacy server→client request or
   * a modern MRTR `input_required` round. Drives era-accurate copy in the
   * pending-request UI. Defaults to `"server-request"` so existing call sites
   * (and stories) keep the legacy semantics unchanged.
   */
  origin;
  resolvePromise;
  /**
   * Rejects the originating call with an error. Set for task-augmented elicit
   * (so the server's `tasks/result` receives the error on decline) and for
   * MRTR-driven elicitations (so a genuine failure aborts the tool call).
   */
  rejectCallback;
  onRemove;
  constructor(request, resolve4, onRemove, reject, origin = "server-request") {
    this.onRemove = onRemove;
    this.id = `elicitation-${crypto.randomUUID()}`;
    this.timestamp = /* @__PURE__ */ new Date();
    this.request = request;
    const relatedTask = request.params?._meta?.[RELATED_TASK_META_KEY2];
    this.taskId = relatedTask?.taskId;
    this.origin = origin;
    this.resolvePromise = resolve4;
    this.rejectCallback = reject;
  }
  /**
   * Reject the elicitation (e.g. when user declines). Only has effect when this
   * request was task-augmented; then the server's tasks/result will receive the error.
   */
  reject(error) {
    if (this.rejectCallback) {
      this.rejectCallback(error);
      this.rejectCallback = void 0;
    }
  }
  /**
   * Respond to the elicitation request with a result
   */
  async respond(result) {
    if (!this.resolvePromise) {
      throw new Error("Request already resolved");
    }
    this.resolvePromise(result);
    this.resolvePromise = void 0;
    this.remove();
  }
  /**
   * Resolve this elicitation as accepted, but only if it is still pending.
   *
   * Used by the URL-mode `notifications/elicitation/complete` handler to
   * auto-advance an open URL elicitation when the server signals the
   * out-of-band flow finished. It is a no-op once the user has already
   * responded — that guard (plus the modal's own once-guard) keeps `respond()`
   * from throwing its "already resolved" error on a race between the manual
   * "I've completed it" click and the server's completion notification.
   */
  completeIfPending() {
    if (this.resolvePromise) {
      void this.respond({ action: "accept" });
    }
  }
  /**
   * Settle a still-pending elicitation as cancelled, without removing it from
   * the queue. Called from `InspectorClient`'s `clearPendingPeerRequests()`,
   * which serves every route that drops the queue wholesale — each route out of
   * a connection, and the top of `connect()` as a backstop for the one route in
   * that settles nothing — so an awaiting caller (notably the error-path
   * `awaitUrlElicitation` that blocks `callTool`) doesn't hang forever. The
   * category, not a count: that set has grown. No-op once already resolved.
   *
   * Deliberately does not call `onRemove`: that caller iterates the queue and
   * clears it itself, so removing here would splice mid-iteration — skipping
   * every other entry and leaving those promises unsettled, the exact hang this
   * method exists to prevent.
   */
  cancel() {
    if (this.resolvePromise) {
      this.resolvePromise({ action: "cancel" });
      this.resolvePromise = void 0;
    }
    this.rejectCallback = void 0;
  }
  /**
   * Remove this pending elicitation from the list
   */
  remove() {
    this.onRemove(this.id);
  }
};

// ../../core/mcp/urlElicitation.ts
import {
  ProtocolErrorCode,
  ProtocolError,
  UrlElicitationRequiredError
} from "@modelcontextprotocol/client";
var UrlElicitationLoopError = class extends Error {
  /** The URL the server repeated. */
  url;
  constructor(url) {
    super(
      `The server asked for the same URL elicitation again (${url}); cancelling the call to avoid a loop.`
    );
    this.name = "UrlElicitationLoopError";
    this.url = url;
  }
};
function getUrlElicitationsFromError(error) {
  if (error instanceof UrlElicitationRequiredError) {
    return error.elicitations ?? [];
  }
  if (error instanceof ProtocolError && error.code === ProtocolErrorCode.UrlElicitationRequired) {
    const data = error.data;
    return data?.elicitations ?? [];
  }
  return null;
}

// ../../core/mcp/toolCallCancelledError.ts
var ToolCallCancelledError = class extends Error {
  /** The name of the tool whose call was cancelled, when known. */
  toolName;
  constructor(toolName) {
    super(
      toolName ? `Tool call "${toolName}" was cancelled.` : "Tool call was cancelled."
    );
    this.name = "ToolCallCancelledError";
    this.toolName = toolName;
  }
};

// ../../core/logging/logger.ts
var noop = () => {
};
function createSilentLogger() {
  const logger = {
    level: "silent",
    fatal: noop,
    error: noop,
    warn: noop,
    info: noop,
    debug: noop,
    trace: noop,
    silent: noop,
    child: () => logger
  };
  return logger;
}
var silentLogger = createSilentLogger();

// ../../core/auth/providers.ts
var MutableRedirectUrlProvider = class {
  redirectUrl = "";
  getRedirectUrl() {
    return this.redirectUrl;
  }
};
var CallbackNavigation = class {
  authorizationUrl = null;
  callback;
  constructor(callback) {
    this.callback = callback;
  }
  navigateToAuthorization(authorizationUrl) {
    this.authorizationUrl = authorizationUrl;
    const result = this.callback(authorizationUrl);
    if (result instanceof Promise) {
      void result;
    }
  }
  getAuthorizationUrl() {
    return this.authorizationUrl;
  }
};
var BaseOAuthClientProvider = class {
  capturedAuthUrl = null;
  eventTarget = null;
  suppressAuthorizationNavigation = false;
  /** Cached after {@link prepareForAuth} for sync SDK `clientMetadata.scope`. */
  cachedScope;
  serverUrl;
  storage;
  redirectUrlProvider;
  navigation;
  clientMetadataUrl;
  constructor(serverUrl, oauthConfig) {
    this.serverUrl = serverUrl;
    this.storage = oauthConfig.storage;
    this.redirectUrlProvider = oauthConfig.redirectUrlProvider;
    this.navigation = oauthConfig.navigation;
    this.clientMetadataUrl = oauthConfig.clientMetadataUrl;
  }
  /**
   * Load persisted scope into {@link cachedScope} before SDK `auth()` (which
   * reads {@link clientMetadata.scope} synchronously).
   */
  async prepareForAuth() {
    this.cachedScope = await this.storage.getScope(this.serverUrl);
  }
  /**
   * Set the event target for dispatching oauthAuthorizationRequired events
   */
  setEventTarget(eventTarget) {
    this.eventTarget = eventTarget;
  }
  /**
   * Get the captured authorization URL (for return value)
   */
  getCapturedAuthUrl() {
    return this.capturedAuthUrl;
  }
  /**
   * Clear the captured authorization URL
   */
  clearCapturedAuthUrl() {
    this.capturedAuthUrl = null;
  }
  /** Capture authorize URL without navigating (step-up confirmation modal). */
  setSuppressAuthorizationNavigation(suppress) {
    this.suppressAuthorizationNavigation = suppress;
  }
  get scope() {
    return this.cachedScope;
  }
  get redirectUrl() {
    return this.redirectUrlProvider.getRedirectUrl();
  }
  get redirect_uris() {
    return [this.redirectUrl];
  }
  get clientMetadata() {
    const metadata = {
      redirect_uris: this.redirect_uris,
      token_endpoint_auth_method: "none",
      grant_types: ["authorization_code", "refresh_token"],
      response_types: ["code"],
      client_name: "MCP Inspector",
      client_uri: "https://github.com/modelcontextprotocol/inspector",
      scope: this.scope ?? "",
      // SEP-837: the Inspector is a locally-hosted app reached over localhost, so
      // it registers as a native client. OIDC-flavored ASes default an omitted
      // `application_type` to `"web"`, which forbids loopback redirect URIs and
      // rejects DCR. (The SDK also infers `"native"` from loopback `redirect_uris`;
      // declaring it explicitly keeps the value visible and correct even when the
      // redirect host is not itself a loopback literal.)
      application_type: "native"
    };
    return metadata;
  }
  state() {
    return generateOAuthState();
  }
  async clientInformation(ctx) {
    const preregistered = await this.storage.getClientInformation(
      this.serverUrl,
      true
    );
    if (preregistered) {
      return preregistered;
    }
    return await this.storage.getClientInformation(
      this.serverUrl,
      false,
      ctx?.issuer
    );
  }
  async saveClientInformation(clientInformation, options) {
    const registrationKind = options && "registrationKind" in options ? options.registrationKind : "dcr";
    const issuer = options && "issuer" in options ? options.issuer : void 0;
    await this.storage.saveClientInformation(
      this.serverUrl,
      clientInformation,
      {
        registrationKind,
        issuer
      }
    );
  }
  async saveScope(scope) {
    await this.storage.saveScope(this.serverUrl, scope);
    this.cachedScope = scope;
  }
  async savePreregisteredClientInformation(clientInformation) {
    await this.storage.savePreregisteredClientInformation(
      this.serverUrl,
      clientInformation
    );
  }
  async tokens(ctx) {
    return await this.storage.getTokens(this.serverUrl, ctx?.issuer);
  }
  async saveTokens(tokens, ctx) {
    await this.storage.saveTokens(this.serverUrl, tokens, {
      issuer: ctx?.issuer
    });
  }
  redirectToAuthorization(authorizationUrl) {
    this.capturedAuthUrl = authorizationUrl;
    if (!this.suppressAuthorizationNavigation) {
      if (this.eventTarget) {
        this.eventTarget.dispatchEvent(
          new CustomEvent("oauthAuthorizationRequired", {
            detail: { url: authorizationUrl }
          })
        );
      }
      this.navigation.navigateToAuthorization(authorizationUrl);
    }
  }
  async saveCodeVerifier(codeVerifier) {
    await this.storage.saveCodeVerifier(this.serverUrl, codeVerifier);
  }
  async codeVerifier() {
    const verifier = await this.storage.getCodeVerifier(this.serverUrl);
    if (!verifier) {
      throw new Error("No code verifier saved for session");
    }
    return verifier;
  }
  async clear() {
    await this.storage.clear(this.serverUrl);
  }
  async getServerMetadata() {
    return this.storage.getServerMetadata(this.serverUrl);
  }
  async saveServerMetadata(metadata) {
    await this.storage.saveServerMetadata(this.serverUrl, metadata);
  }
  /**
   * SEP-2352 discovery-state round-trip. The SDK persists RFC 9728/8414 discovery
   * here (alongside the code verifier) so that on the authorization-code callback
   * leg it can compare the resolved AS `issuer` against the one recorded at
   * redirect time and reject a mismatch (`AuthorizationServerMismatchError`).
   * Without these two methods the SDK only `console.warn`s and the binding check
   * is inactive.
   */
  async saveDiscoveryState(state) {
    await this.storage.saveDiscoveryState(this.serverUrl, state);
  }
  async discoveryState() {
    return this.storage.getDiscoveryState(this.serverUrl);
  }
  /**
   * SEP-2352 credential invalidation. The SDK calls this to drop credentials the
   * server has rejected; hosts also call `'discovery'` on repeated 401s so a
   * changed `authorization_servers` list is re-fetched.
   */
  async invalidateCredentials(scope) {
    switch (scope) {
      case "all":
        await this.storage.clear(this.serverUrl);
        return;
      case "client":
        await this.storage.clearClientInformation(this.serverUrl);
        return;
      case "tokens":
        await this.storage.clearTokens(this.serverUrl);
        return;
      case "verifier":
        await this.storage.clearCodeVerifier(this.serverUrl);
        return;
      case "discovery":
        await this.storage.clearDiscoveryState(this.serverUrl);
        return;
    }
  }
};

// ../../core/auth/types.ts
function authProtocolFromEnterpriseManaged(enterpriseManaged) {
  return enterpriseManaged ? "ema" : "standard";
}
var EMPTY_OAUTH_FLOW_STATE = {
  completedAt: null,
  isInitiatingAuth: false,
  oauthTokens: null,
  oauthStep: "authorization_code",
  oauthMetadata: null,
  resourceMetadata: null,
  resourceMetadataError: null,
  resource: null,
  authServerUrl: null,
  oauthClientInfo: null,
  authorizationUrl: null,
  authorizationCode: "",
  latestError: null,
  statusMessage: null,
  validationError: null
};

// ../../core/auth/mcpAuth.ts
import { auth as sdkAuth } from "@modelcontextprotocol/client";
async function mcpAuth(provider, options) {
  if (options.forceReauthorization && options.authorizationCode !== void 0) {
    throw new Error(
      "forceReauthorization cannot be combined with authorizationCode"
    );
  }
  return sdkAuth(provider, {
    serverUrl: options.serverUrl,
    authorizationCode: options.authorizationCode,
    iss: options.iss,
    scope: options.scope,
    resourceMetadataUrl: options.resourceMetadataUrl,
    fetchFn: options.fetchFn,
    skipIssuerMetadataValidation: options.skipIssuerMetadataValidation,
    forceReauthorization: options.forceReauthorization
  });
}

// ../../core/auth/ema/clientConfigError.ts
function emaClientNotConfiguredMessage(reason) {
  if (reason === "disabled") {
    return 'This server uses enterprise-managed authorization, but Enterprise IdP is turned off in Client Settings. Open Client Settings, enable "Enterprise IdP configuration", then try connecting again.';
  }
  return "This server uses enterprise-managed authorization, but the Inspector client IdP is not configured. Open Client Settings, enable Enterprise IdP, and set issuer, client ID, and client secret.";
}
var EmaClientNotConfiguredError = class extends Error {
  reason;
  constructor(reason) {
    super(emaClientNotConfiguredMessage(reason));
    this.name = "EmaClientNotConfiguredError";
    this.reason = reason;
  }
};
function isEmaClientNotConfiguredError(err) {
  return err instanceof EmaClientNotConfiguredError;
}

// ../../core/auth/ema/idpOidc.ts
import {
  discoverAuthorizationServerMetadata,
  exchangeAuthorization,
  startAuthorization
} from "@modelcontextprotocol/client";
import { OAuthMetadataSchema } from "@modelcontextprotocol/core";

// ../../core/auth/ema/constants.ts
var GRANT_TYPE_JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer";
var IDP_OIDC_SCOPES = "openid offline_access";

// ../../core/auth/ema/jwt.ts
function decodeBase64Url(segment) {
  const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
  const padded = base64.padEnd(
    base64.length + (4 - base64.length % 4) % 4,
    "="
  );
  const binary = atob(padded);
  const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
  return new TextDecoder().decode(bytes);
}
function jwtExpiresAtMs(token) {
  const parts = token.split(".");
  if (parts.length < 2) return void 0;
  try {
    const payload = JSON.parse(decodeBase64Url(parts[1]));
    if (typeof payload.exp === "number" && Number.isFinite(payload.exp)) {
      return payload.exp * 1e3;
    }
  } catch {
    return void 0;
  }
  return void 0;
}
function isJwtExpired(token, skewMs = 6e4, nowMs = Date.now()) {
  const exp = jwtExpiresAtMs(token);
  if (exp === void 0) return false;
  return nowMs >= exp - skewMs;
}

// ../../core/auth/ema/storage.ts
function normalizeIdpIssuer(issuer) {
  return issuer.replace(/\/$/, "");
}
function idpOAuthStorageKey(issuer) {
  return `ema-idp:${normalizeIdpIssuer(issuer)}`;
}

// ../../core/auth/ema/tokenEndpoint.ts
import { selectClientAuthMethod } from "@modelcontextprotocol/client";
async function parseOAuthTokenErrorResponse(response, step) {
  let body;
  try {
    body = await response.json();
  } catch {
    return new Error(`${step}: token request failed (HTTP ${response.status})`);
  }
  if (typeof body === "object" && body !== null) {
    const record = body;
    const parts = [step];
    if (record.error) parts.push(`error=${record.error}`);
    if (record.error_description) parts.push(record.error_description);
    if (parts.length > 1) {
      return new Error(parts.join(": "));
    }
  }
  return new Error(`${step}: token request failed (HTTP ${response.status})`);
}
function applyClientAuth(metadata, clientInformation, headers, body) {
  const supported = metadata?.token_endpoint_auth_methods_supported ?? [];
  const method = selectClientAuthMethod(clientInformation, supported);
  if (method === "client_secret_basic" && clientInformation.client_secret) {
    const credentials = btoa(
      `${clientInformation.client_id}:${clientInformation.client_secret}`
    );
    headers.set("Authorization", `Basic ${credentials}`);
    return;
  }
  if (method === "client_secret_post" && clientInformation.client_secret) {
    body.set("client_id", clientInformation.client_id);
    body.set("client_secret", clientInformation.client_secret);
    return;
  }
  body.set("client_id", clientInformation.client_id);
}
async function postOAuthTokenRequest(tokenUrl, body, metadata, clientInformation, fetchFn) {
  const headers = new Headers({
    "Content-Type": "application/x-www-form-urlencoded",
    Accept: "application/json"
  });
  applyClientAuth(metadata, clientInformation, headers, body);
  return (fetchFn ?? fetch)(tokenUrl, {
    method: "POST",
    headers,
    body
  });
}

// ../../core/auth/ema/idpOidc.ts
function idpClientInformation(idp) {
  return {
    client_id: idp.clientId,
    client_secret: idp.clientSecret,
    token_endpoint_auth_method: "client_secret_post"
  };
}
async function resolveIdpMetadata(issuer, storage, fetchFn) {
  const storageKey = idpOAuthStorageKey(issuer);
  const cached = await storage.getServerMetadata(storageKey);
  if (cached?.token_endpoint) {
    return cached;
  }
  return discoverIdpMetadata(issuer, fetchFn);
}
async function discoverIdpMetadata(issuer, fetchFn) {
  const issuerUrl = parseHttpUrl(issuer, "EMA IdP issuer (Client Settings)");
  const metadata = await discoverAuthorizationServerMetadata(issuerUrl, {
    fetchFn
  });
  if (!metadata) {
    throw new Error(
      `Failed to discover OIDC metadata for IdP issuer ${issuer}`
    );
  }
  return OAuthMetadataSchema.parse(metadata);
}
async function startIdpOidcAuthorization(params) {
  const issuer = normalizeIdpIssuer(params.idp.issuer);
  const metadata = await resolveIdpMetadata(
    issuer,
    params.storage,
    params.fetchFn
  );
  const clientInformation = idpClientInformation(params.idp);
  const storageKey = idpOAuthStorageKey(issuer);
  const state = generateOAuthState();
  const issuerUrl = parseHttpUrl(issuer, "EMA IdP issuer (Client Settings)");
  const { authorizationUrl, codeVerifier } = await startAuthorization(
    issuerUrl,
    {
      metadata,
      clientInformation,
      redirectUrl: params.redirectUrl,
      scope: IDP_OIDC_SCOPES,
      state
    }
  );
  await params.storage.saveCodeVerifier(storageKey, codeVerifier);
  await params.storage.savePreregisteredClientInformation(
    storageKey,
    clientInformation
  );
  await params.storage.saveServerMetadata(storageKey, metadata);
  return { authorizationUrl };
}
async function completeIdpOidcAuthorization(params) {
  const issuer = normalizeIdpIssuer(params.idp.issuer);
  const storageKey = idpOAuthStorageKey(issuer);
  const metadata = await params.storage.getServerMetadata(storageKey);
  if (!metadata) {
    throw new Error("IdP OAuth metadata not found \u2014 restart EMA IdP login");
  }
  const clientInformation = await params.storage.getClientInformation(storageKey, true) ?? await params.storage.getClientInformation(storageKey);
  if (!clientInformation) {
    throw new Error("IdP client information not found \u2014 restart EMA IdP login");
  }
  const codeVerifier = await params.storage.getCodeVerifier(storageKey);
  if (!codeVerifier) {
    throw new Error("IdP PKCE verifier not found \u2014 restart EMA IdP login");
  }
  const issuerUrl = parseHttpUrl(issuer, "EMA IdP issuer (Client Settings)");
  const tokens = await exchangeAuthorization(issuerUrl, {
    metadata,
    clientInformation,
    authorizationCode: params.authorizationCode,
    iss: params.iss,
    codeVerifier,
    redirectUri: params.redirectUrl,
    fetchFn: params.fetchFn
  });
  const idToken = tokens.id_token;
  if (!idToken) {
    throw new Error("IdP token response did not include an ID Token");
  }
  await params.storage.clearCodeVerifier(storageKey);
  const idTokenExpiresAt = jwtExpiresAtMs(idToken);
  await params.storage.saveIdpSession(issuer, {
    idToken,
    refreshToken: tokens.refresh_token,
    idTokenExpiresAt
  });
  return {
    idToken,
    refreshToken: tokens.refresh_token,
    idTokenExpiresAt
  };
}
async function refreshIdpOidcSession(params) {
  const issuer = normalizeIdpIssuer(params.idp.issuer);
  const session = await params.storage.getIdpSession(issuer);
  if (!session?.refreshToken) {
    throw new Error("IdP refresh token not available");
  }
  const metadata = await resolveIdpMetadata(
    issuer,
    params.storage,
    params.fetchFn
  );
  if (!metadata.token_endpoint) {
    throw new Error("IdP metadata missing token_endpoint");
  }
  const body = new URLSearchParams({
    grant_type: "refresh_token",
    refresh_token: session.refreshToken
  });
  const response = await postOAuthTokenRequest(
    parseHttpUrl(
      metadata.token_endpoint,
      "IdP token_endpoint (from OIDC discovery)"
    ),
    body,
    metadata,
    idpClientInformation(params.idp),
    params.fetchFn
  );
  if (!response.ok) {
    throw await parseOAuthTokenErrorResponse(
      response,
      "EMA IdP refresh (OIDC refresh_token grant)"
    );
  }
  const json = await response.json();
  const idToken = json.id_token;
  if (!idToken) {
    throw new Error("IdP refresh did not return an ID Token");
  }
  await params.storage.saveIdpSession(issuer, {
    idToken,
    refreshToken: json.refresh_token ?? session.refreshToken,
    idTokenExpiresAt: jwtExpiresAtMs(idToken)
  });
  return idToken;
}
async function getValidIdToken(params) {
  const issuer = normalizeIdpIssuer(params.idp.issuer);
  const session = await params.storage.getIdpSession(issuer);
  if (!session?.idToken) {
    return void 0;
  }
  if (!isJwtExpired(session.idToken)) {
    return session.idToken;
  }
  if (!session.refreshToken) {
    return void 0;
  }
  try {
    return await refreshIdpOidcSession(params);
  } catch {
    return void 0;
  }
}

// ../../core/auth/ema/resourceContext.ts
import {
  discoverAuthorizationServerMetadata as discoverAuthorizationServerMetadata3,
  discoverOAuthProtectedResourceMetadata,
  selectResourceURL
} from "@modelcontextprotocol/client";

// ../../core/auth/discovery.ts
import { discoverAuthorizationServerMetadata as discoverAuthorizationServerMetadata2 } from "@modelcontextprotocol/client";
function getAuthorizationServerUrl(serverUrl, resourceMetadata) {
  const first = resourceMetadata?.authorization_servers?.[0];
  if (first) {
    return parseHttpUrl(first, "protected resource authorization_servers[0]");
  }
  try {
    return new URL("/", serverUrl);
  } catch (err) {
    const detail = err instanceof Error ? err.message : String(err);
    throw new Error(`Invalid MCP server URL: "${serverUrl}" (${detail})`, {
      cause: err
    });
  }
}

// ../../core/auth/ema/resourceContext.ts
function resolveEmaScopes(resourceMetadata, configuredScope) {
  const trimmed = configuredScope?.trim();
  if (trimmed) return trimmed;
  const supported = resourceMetadata?.scopes_supported;
  if (supported && supported.length > 0) {
    return supported.join(" ");
  }
  return void 0;
}
async function discoverEmaResourceContext(serverUrl, configuredScope, fetchFn) {
  const resourceMetadata = await discoverOAuthProtectedResourceMetadata(
    serverUrl,
    void 0,
    fetchFn
  );
  if (!resourceMetadata.resource) {
    throw new Error(
      "EMA requires protected resource metadata with a resource identifier"
    );
  }
  const resourceAsUrl = getAuthorizationServerUrl(serverUrl, resourceMetadata);
  const resourceUrl = await selectResourceURL(
    serverUrl,
    // `selectResourceURL` only reads `clientMetadata.scope` off the provider, so
    // we pass a minimal stub carrying just that. A partial literal has too little
    // overlap with the full `OAuthClientProvider` interface for a single cast, so
    // the double cast bridges the deliberately-incomplete shape here.
    {
      clientMetadata: {
        scope: configuredScope ?? "",
        redirect_uris: []
      }
    },
    resourceMetadata
  );
  const scope = resolveEmaScopes(resourceMetadata, configuredScope);
  return {
    resourceMetadata,
    resourceAsUrl,
    resourceUrl: resourceUrl ?? void 0,
    scope
  };
}
async function discoverResourceAsMetadata(resourceAsUrl, fetchFn) {
  const metadata = await discoverAuthorizationServerMetadata3(resourceAsUrl, {
    fetchFn
  });
  if (!metadata) {
    throw new Error(
      "Failed to discover resource authorization server metadata"
    );
  }
  return metadata;
}

// ../../core/auth/ema/wire.ts
import { discoverAndRequestJwtAuthGrant } from "@modelcontextprotocol/client";
import { OAuthTokensSchema } from "@modelcontextprotocol/core";
async function exchangeIdJag(params) {
  const issuer = normalizeIdpIssuer(params.idp.issuer);
  const resource = params.resource.trim();
  if (!resource) {
    throw new Error("EMA leg 2 requires a resource identifier");
  }
  let result;
  try {
    result = await discoverAndRequestJwtAuthGrant({
      idpUrl: issuer,
      audience: params.audience,
      resource,
      idToken: params.idToken,
      clientId: params.idp.clientId,
      clientSecret: params.idp.clientSecret,
      scope: params.scope,
      fetchFn: params.fetchFn
    });
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    if (/Failed to discover token endpoint/i.test(message)) {
      throw new Error("IdP metadata missing token_endpoint", { cause: err });
    }
    if (/Invalid token exchange response/i.test(message)) {
      throw new Error("IdP token exchange did not return an ID-JAG", {
        cause: err
      });
    }
    throw new Error(`EMA leg 2 (IdP token exchange for ID-JAG): ${message}`, {
      cause: err
    });
  }
  if (!result.jwtAuthGrant) {
    throw new Error("IdP token exchange did not return an ID-JAG");
  }
  return result.jwtAuthGrant;
}
async function redeemIdJagForAccessToken(params) {
  const asMetadata = await discoverResourceAsMetadata(
    params.resourceAsUrl,
    params.fetchFn
  );
  if (!asMetadata.token_endpoint) {
    throw new Error("Resource AS metadata missing token_endpoint");
  }
  const clientInformation = {
    client_id: params.resourceClientId,
    ...params.resourceClientSecret && {
      client_secret: params.resourceClientSecret
    },
    token_endpoint_auth_method: params.resourceClientSecret ? "client_secret_post" : "none"
  };
  const body = new URLSearchParams({
    grant_type: GRANT_TYPE_JWT_BEARER,
    assertion: params.idJag
  });
  if (params.scope) {
    body.set("scope", params.scope);
  }
  if (params.resource) {
    body.set("resource", params.resource);
  }
  const response = await postOAuthTokenRequest(
    parseHttpUrl(
      asMetadata.token_endpoint,
      "resource authorization server token_endpoint (from AS discovery)"
    ),
    body,
    asMetadata,
    clientInformation,
    params.fetchFn
  );
  if (!response.ok) {
    throw await parseOAuthTokenErrorResponse(
      response,
      "EMA leg 3 (resource AS JWT bearer grant)"
    );
  }
  return OAuthTokensSchema.parse(await response.json());
}

// ../../core/auth/ema/emaFlow.ts
function wrapEmaMintError(err) {
  const message = err instanceof Error ? err.message : String(err);
  return new Error(`EMA legs 2\u20133 (resource token mint): ${message}`, {
    cause: err
  });
}
async function mintEmaResourceTokens(config, resourceContext) {
  const ctx = resourceContext ?? await discoverEmaResourceContext(
    config.serverUrl,
    config.scope,
    config.fetchFn
  );
  if (!config.resourceClientId) {
    throw new Error(
      "EMA requires resource authorization server clientId (per-server oauth.clientId)"
    );
  }
  if (!config.resourceClientSecret?.trim()) {
    throw new Error(
      "EMA requires resource authorization server client secret in server OAuth settings (Test Client secret from xaa.dev resource registration)"
    );
  }
  const idToken = await getValidIdToken({
    idp: config.idp,
    storage: config.storage,
    fetchFn: config.fetchFn
  });
  if (!idToken) {
    throw new Error("Valid IdP ID Token required for EMA token mint");
  }
  const audience = ctx.resourceAsUrl.href.replace(/\/$/, "");
  const resource = ctx.resourceUrl?.href ?? ctx.resourceMetadata.resource;
  const idJag = await exchangeIdJag({
    idp: config.idp,
    idToken,
    audience,
    resource,
    scope: ctx.scope,
    fetchFn: config.fetchFn
  });
  return redeemIdJagForAccessToken({
    resourceAsUrl: ctx.resourceAsUrl,
    idJag,
    resourceClientId: config.resourceClientId,
    resourceClientSecret: config.resourceClientSecret,
    resource: ctx.resourceUrl?.href,
    scope: ctx.scope,
    fetchFn: config.fetchFn
  });
}
async function trySilentEmaAuth(config) {
  const idToken = await getValidIdToken({
    idp: config.idp,
    storage: config.storage,
    fetchFn: config.fetchFn
  });
  if (!idToken) {
    return { status: "no_idp_session" };
  }
  try {
    const tokens = await mintEmaResourceTokens(config);
    await config.storage.saveTokens(config.serverUrl, tokens, {
      enterpriseManaged: true
    });
    return { status: "success" };
  } catch (err) {
    return { status: "mint_failed", error: wrapEmaMintError(err) };
  }
}
async function startEmaIdpAuthorization(config) {
  const { authorizationUrl } = await startIdpOidcAuthorization({
    idp: config.idp,
    redirectUrl: config.redirectUrl,
    storage: config.storage,
    fetchFn: config.fetchFn
  });
  return authorizationUrl;
}
async function completeEmaIdpAuthorizationAndMint(config, authorizationCode, iss) {
  try {
    await completeIdpOidcAuthorization({
      idp: config.idp,
      authorizationCode,
      iss,
      redirectUrl: config.redirectUrl,
      storage: config.storage,
      fetchFn: config.fetchFn
    });
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    throw new Error(`EMA leg 1 (IdP authorization code exchange): ${message}`, {
      cause: err
    });
  }
  let tokens;
  try {
    tokens = await mintEmaResourceTokens(config);
  } catch (err) {
    throw wrapEmaMintError(err);
  }
  await config.storage.saveTokens(config.serverUrl, tokens, {
    enterpriseManaged: true
  });
  return tokens;
}
async function refreshEmaResourceTokens(config) {
  const idToken = await getValidIdToken({
    idp: config.idp,
    storage: config.storage,
    fetchFn: config.fetchFn
  });
  if (!idToken) {
    return void 0;
  }
  const tokens = await mintEmaResourceTokens(config);
  await config.storage.saveTokens(config.serverUrl, tokens, {
    enterpriseManaged: true
  });
  return tokens;
}

// ../../core/auth/ema/idpSession.ts
async function getEmaIdpLoginState(storage, issuer) {
  const normalized = normalizeIdpIssuer(issuer);
  if (!normalized) return "none";
  const session = await storage.getIdpSession(normalized);
  if (!session?.idToken) return "none";
  if (!isJwtExpired(session.idToken)) return "logged_in";
  if (session.refreshToken) return "logged_in";
  return "expired";
}

// ../../core/auth/connection-state.ts
function isAccessTokenUsable(tokens) {
  if (!tokens?.access_token) return false;
  return !isJwtExpired(tokens.access_token);
}
function resolveClient(preregistered, dynamic, dynamicRegistrationKind) {
  if (preregistered?.client_id) {
    return {
      registrationKind: "static",
      clientId: preregistered.client_id,
      hasClientSecret: preregistered.client_secret !== void 0 && preregistered.client_secret !== ""
    };
  }
  if (!dynamic?.client_id) return void 0;
  return {
    ...dynamicRegistrationKind && {
      registrationKind: dynamicRegistrationKind
    },
    clientId: dynamic.client_id,
    hasClientSecret: dynamic.client_secret !== void 0 && dynamic.client_secret !== ""
  };
}
function resolveGrantedScope(tokens, storageScope) {
  const tokenScope = typeof tokens?.scope === "string" ? tokens.scope.trim() : void 0;
  if (tokenScope) return tokenScope;
  const stored = typeof storageScope === "string" ? storageScope.trim() : void 0;
  return stored || void 0;
}
async function buildOAuthConnectionState(params) {
  const { serverUrl, storage, flowState } = params;
  const protocol = params.protocol;
  const storedTokens = await storage.getTokens(serverUrl);
  const flowTokens = flowState?.oauthTokens ?? void 0;
  const tokens = flowTokens && isAccessTokenUsable(flowTokens) ? flowTokens : storedTokens && isAccessTokenUsable(storedTokens) ? storedTokens : flowTokens ?? storedTokens;
  const preregistered = await storage.getClientInformation(serverUrl, true);
  const dynamic = await storage.getClientInformation(serverUrl);
  const storageScope = await storage.getScope(serverUrl);
  const serverMetadata = await storage.getServerMetadata(serverUrl);
  const authorized = isAccessTokenUsable(tokens);
  const grantedScope = resolveGrantedScope(tokens, storageScope);
  const registrationKind = await storage.getClientRegistrationKind(serverUrl);
  const dynamicRegistrationKind = registrationKind === "dcr" || registrationKind === "cimd" ? registrationKind : void 0;
  const client = resolveClient(
    preregistered,
    dynamic,
    preregistered?.client_id ? void 0 : dynamicRegistrationKind
  );
  const state = {
    authorized,
    protocol,
    serverUrl,
    ...params.configuredScope?.trim() && {
      configuredScope: params.configuredScope.trim()
    },
    ...grantedScope && { grantedScope },
    ...tokens && { tokens },
    ...client && { client },
    ...serverMetadata && { authorizationServerMetadata: serverMetadata }
  };
  if (protocol === "ema" && params.enterpriseManagedAuth?.idp) {
    const idp = params.enterpriseManagedAuth.idp;
    const issuer = normalizeIdpIssuer(idp.issuer);
    const idpSession = await getEmaIdpLoginState(storage, issuer);
    const idpMetadata = await storage.getServerMetadata(
      idpOAuthStorageKey(issuer)
    );
    state.ema = {
      idpIssuer: issuer,
      idpClientId: idp.clientId,
      idpSession,
      ...idpMetadata && { idpMetadata }
    };
    state.enterpriseManaged = true;
  }
  return state;
}
function isServerOAuthConfigured(config) {
  return config.enterpriseManaged === true || !!config.clientId?.trim() || !!config.clientSecret?.trim() || !!config.scope?.trim() || !!config.clientMetadataUrl?.trim();
}
async function hasPersistedOAuthServerState(storage, serverUrl) {
  const [tokens, preregistered, dynamic] = await Promise.all([
    storage.getTokens(serverUrl),
    storage.getClientInformation(serverUrl, true),
    storage.getClientInformation(serverUrl)
  ]);
  return !!(tokens?.access_token || preregistered?.client_id || dynamic?.client_id);
}
function protocolFromOAuthConfig(config) {
  return authProtocolFromEnterpriseManaged(config.enterpriseManaged);
}

// ../../core/auth/cimd.ts
import {
  discoverAuthorizationServerMetadata as discoverAuthorizationServerMetadata4,
  discoverOAuthProtectedResourceMetadata as discoverOAuthProtectedResourceMetadata2
} from "@modelcontextprotocol/client";
async function ensureCimdClientRegistration(params) {
  const clientMetadataUrl = params.provider.clientMetadataUrl?.trim();
  if (!clientMetadataUrl) return;
  const existing = await params.provider.clientInformation();
  if (existing?.client_id) return;
  let resourceMetadata;
  try {
    resourceMetadata = await discoverOAuthProtectedResourceMetadata2(
      params.serverUrl
    );
  } catch {
    resourceMetadata = void 0;
  }
  const authServerUrl = getAuthorizationServerUrl(
    params.serverUrl,
    resourceMetadata
  );
  const metadata = await discoverAuthorizationServerMetadata4(authServerUrl, {
    ...params.fetchFn && { fetchFn: params.fetchFn }
  });
  if (!metadata?.client_id_metadata_document_supported) return;
  const clientInformation = {
    client_id: clientMetadataUrl
  };
  await params.provider.saveClientInformation(clientInformation, {
    registrationKind: "cimd"
  });
}

// ../../core/auth/ema/transportProvider.ts
function isAccessTokenUsable2(tokens, skewMs = 6e4) {
  if (!tokens.access_token) return false;
  return !isJwtExpired(tokens.access_token, skewMs);
}
var EmaTransportOAuthProvider = class {
  inner;
  emaConfig;
  constructor(inner, emaConfig) {
    this.inner = inner;
    this.emaConfig = emaConfig;
  }
  get redirectUrl() {
    return this.inner.redirectUrl;
  }
  get clientMetadataUrl() {
    return this.inner.clientMetadataUrl;
  }
  get clientMetadata() {
    return this.inner.clientMetadata;
  }
  state() {
    return this.inner.state();
  }
  clientInformation() {
    return this.inner.clientInformation();
  }
  saveClientInformation(clientInformation) {
    return this.inner.saveClientInformation(clientInformation);
  }
  async tokens() {
    const stored = await this.inner.tokens();
    if (stored && isAccessTokenUsable2(stored)) {
      return stored;
    }
    const refreshed = await refreshEmaResourceTokens(this.emaConfig);
    if (refreshed) {
      return refreshed;
    }
    return void 0;
  }
  saveTokens(tokens) {
    return this.emaConfig.storage.saveTokens(this.emaConfig.serverUrl, tokens, {
      enterpriseManaged: true
    });
  }
  async redirectToAuthorization(_authorizationUrl) {
    const idpAuthorizationUrl = await startEmaIdpAuthorization(this.emaConfig);
    this.inner.clearCapturedAuthUrl();
    this.inner.redirectToAuthorization(idpAuthorizationUrl);
  }
  saveCodeVerifier(codeVerifier) {
    return this.inner.saveCodeVerifier(codeVerifier);
  }
  codeVerifier() {
    return this.inner.codeVerifier();
  }
};

// ../../core/auth/scopes.ts
import {
  computeScopeUnion as sdkComputeScopeUnion,
  isStrictScopeSuperset as sdkIsStrictScopeSuperset
} from "@modelcontextprotocol/client";
var computeScopeUnion = sdkComputeScopeUnion;
var isStrictScopeSuperset = sdkIsStrictScopeSuperset;
function resolvePersistedScopeAfterGrant(grantedScope, requestedScope) {
  const granted = grantedScope?.trim();
  if (granted) {
    return granted;
  }
  const requested = requestedScope?.trim();
  return requested || void 0;
}
function resolveEffectiveGrantedScope(storedScope, tokenScope) {
  const granted = tokenScope?.trim();
  if (granted) {
    return granted;
  }
  return computeScopeUnion(storedScope, tokenScope);
}

// ../../core/auth/oauthUx.ts
function isStandardOAuthStepUp(challenge, options) {
  return challenge.reason === "insufficient_scope" && !options?.enterpriseManaged;
}
function isEmaStepUp(challenge, options) {
  return challenge.reason === "insufficient_scope" && options?.enterpriseManaged === true;
}
function isStepUpConfirmation(challenge, options) {
  return isStandardOAuthStepUp(challenge, options) || isEmaStepUp(challenge, options);
}
function stepUpConfirmMessage(challenge, options) {
  const toolName = challenge.context?.toolName?.trim();
  if (toolName) {
    return options?.enterpriseManaged ? `Tool "${toolName}" needs additional permissions from your organization before it can continue.` : `Tool "${toolName}" needs additional OAuth scopes before it can continue.`;
  }
  const additional = challenge.requiredScopes?.filter(Boolean);
  if (additional?.length) {
    const label = additional.length === 1 ? "scope" : "scopes";
    return options?.enterpriseManaged ? `This operation needs additional organization ${label}: ${additional.join(", ")}.` : `This operation needs additional ${label}: ${additional.join(", ")}.`;
  }
  return options?.enterpriseManaged ? "This operation needs additional permissions from your organization before it can continue." : "This operation needs additional OAuth scopes before it can continue.";
}
function stepUpFollowUpMessage(options) {
  return options?.enterpriseManaged ? "Inspector will request the additional permissions from your enterprise identity provider. You may be asked to sign in if your organization session expired." : "You will be redirected to authorize, then returned to the inspector.";
}
function stepUpAuthorizeActionLabel(options) {
  return options?.enterpriseManaged ? "Authorize" : "Authorize (opens browser)";
}
function stepUpModalTitle(options) {
  return options?.enterpriseManaged ? "Additional organization permissions required" : "Additional permissions required";
}
function emaStepUpFailureMessage(detail) {
  return detail?.trim() ? detail : "Could not obtain the additional permissions from your organization.";
}
function stepUpInsufficientScopeMessage(challenge) {
  const toolName = challenge.context?.toolName?.trim();
  if (toolName) {
    return `Authorization completed, but required permissions for tool "${toolName}" were not granted. Grant the requested scopes on the authorization server, then retry.`;
  }
  const missing = challenge.authorizationScopes?.filter(Boolean) ?? challenge.requiredScopes?.filter(Boolean);
  if (missing?.length) {
    return `Authorization completed, but required scopes were not granted (${missing.join(", ")}). Grant the requested permissions on the authorization server, then retry your action.`;
  }
  return "Authorization completed, but the required permissions were not granted. Grant the requested scopes on the authorization server, then retry your action.";
}
function lostAuthorizationStateTitle() {
  return "Authorization state was lost";
}
function lostAuthorizationStateMessage(options) {
  const target = options?.serverName ? `"${options.serverName}"` : "this server";
  return `The stored authorization state for ${target} was lost before the authorization server sent you back, so the sign-in could not be completed. This usually means a new browser session, a different tab, or authorization state that was cleared while the flow was in progress. Authorize again to reconnect.`;
}
function issuerMismatchTitle() {
  return "Authorization server mismatch";
}
var MAX_DISPLAYED_ISSUER_LENGTH = 120;
function truncateIssuerForDisplay(issuer) {
  return issuer.length > MAX_DISPLAYED_ISSUER_LENGTH ? `${issuer.slice(0, MAX_DISPLAYED_ISSUER_LENGTH)}\u2026` : issuer;
}
function issuerMismatchMessage(options) {
  const target = options.serverName ? `"${options.serverName}"` : "this server";
  return `Authorization for ${target} was stopped: the flow started at ${truncateIssuerForDisplay(options.recordedIssuer)} but the callback resolved ${truncateIssuerForDisplay(options.currentIssuer)}. The authorization code was not exchanged. Verify the server's authorization configuration before trying again.`;
}
function issuerBindingFailureCopy(failure, options) {
  if (failure.kind === "lost_authorization_state") {
    return {
      title: lostAuthorizationStateTitle(),
      message: lostAuthorizationStateMessage(options)
    };
  }
  return {
    title: issuerMismatchTitle(),
    message: issuerMismatchMessage({
      recordedIssuer: failure.recordedIssuer,
      currentIssuer: failure.currentIssuer,
      serverName: options?.serverName
    })
  };
}

// ../../core/mcp/oauthManager.ts
var OAuthManager = class {
  params;
  oauthConfig;
  oauthFlowState = null;
  /** SEP-2350 union scope pending until interactive step-up completes. */
  pendingAuthorizationScope;
  authChallengeMutex = Promise.resolve();
  constructor(params) {
    this.params = params;
    this.oauthConfig = { ...params.initialConfig };
    this.pendingAuthorizationScope = void 0;
  }
  setOAuthConfig(config) {
    this.oauthConfig = {
      ...this.oauthConfig,
      ...config
    };
  }
  getServerUrl() {
    return this.params.getServerUrl();
  }
  /**
   * Return the configured OAuth storage, throwing a clear error if it is
   * absent. Used on the EMA re-mint/persist paths, which are only reached for
   * a fully-configured OAuth-capable server (storage present).
   */
  requireStorage() {
    const storage = this.oauthConfig.storage;
    if (!storage) {
      throw new Error("OAuth storage is required for this operation.");
    }
    return storage;
  }
  async createOAuthProvider() {
    if (!this.oauthConfig.storage || !this.oauthConfig.redirectUrlProvider || !this.oauthConfig.navigation) {
      throw new Error(
        "OAuth environment components (storage, navigation, redirectUrlProvider) are required."
      );
    }
    const serverUrl = this.getServerUrl();
    const provider = new BaseOAuthClientProvider(serverUrl, {
      storage: this.oauthConfig.storage,
      redirectUrlProvider: this.oauthConfig.redirectUrlProvider,
      navigation: this.oauthConfig.navigation,
      clientMetadataUrl: this.oauthConfig.clientMetadataUrl
    });
    provider.setEventTarget(this.params.getEventTarget());
    await provider.prepareForAuth();
    const storedScope = provider.scope;
    if (storedScope === void 0 && this.oauthConfig.scope) {
      await provider.saveScope(this.oauthConfig.scope);
    }
    if (this.oauthConfig.clientId) {
      const clientInfo = {
        client_id: this.oauthConfig.clientId,
        ...this.oauthConfig.clientSecret && {
          client_secret: this.oauthConfig.clientSecret
        }
      };
      await provider.savePreregisteredClientInformation(clientInfo);
    }
    return provider;
  }
  /** Whether this connection uses enterprise-managed authorization (EMA). */
  isEnterpriseManaged() {
    return this.oauthConfig.enterpriseManaged === true;
  }
  async getEmaFlowConfig() {
    if (!this.oauthConfig.storage || !this.oauthConfig.redirectUrlProvider) {
      throw new Error(
        "OAuth environment components (storage, redirectUrlProvider) are required."
      );
    }
    const idp = this.params.enterpriseManagedAuth?.idp;
    if (!idp) {
      const install = this.params.installEnterpriseManagedAuth;
      throw new EmaClientNotConfiguredError(
        install?.enabled === false && install.idp ? "disabled" : "not_configured"
      );
    }
    const storedScope = await this.oauthConfig.storage.getScope(
      this.getServerUrl()
    );
    return {
      serverUrl: this.getServerUrl(),
      idp,
      resourceClientId: this.oauthConfig.clientId,
      resourceClientSecret: this.oauthConfig.clientSecret,
      scope: computeScopeUnion(this.oauthConfig.scope, storedScope) || this.oauthConfig.scope,
      redirectUrl: this.oauthConfig.redirectUrlProvider.getRedirectUrl(),
      storage: this.oauthConfig.storage,
      fetchFn: this.params.effectiveAuthFetch
    };
  }
  /** Attempt silent EMA (cached IdP session + legs 2–3). */
  async trySilentEnterpriseManagedAuth() {
    if (!this.isEnterpriseManaged()) return false;
    const result = await trySilentEmaAuth(await this.getEmaFlowConfig());
    if (result.status === "success") return true;
    if (result.status === "mint_failed") throw result.error;
    return false;
  }
  async authenticateEnterpriseManaged() {
    const config = await this.getEmaFlowConfig();
    const silent = await trySilentEmaAuth(config);
    if (silent.status === "success") {
      return void 0;
    }
    if (silent.status === "mint_failed") {
      throw silent.error;
    }
    const authorizationUrl = await startEmaIdpAuthorization(config);
    const stateParam = authorizationUrl.searchParams.get("state");
    if (stateParam && this.params.onBeforeOAuthRedirect) {
      const parsedState = parseOAuthState(stateParam);
      if (parsedState?.authId) {
        await this.params.onBeforeOAuthRedirect(parsedState.authId);
      }
    }
    this.requireNavigation().navigateToAuthorization(authorizationUrl);
    await this.recordAuthorizationCodeFlowState(authorizationUrl);
    return authorizationUrl;
  }
  async authenticate() {
    if (this.isEnterpriseManaged()) {
      return this.authenticateEnterpriseManaged();
    }
    const provider = await this.createOAuthProvider();
    const serverUrl = this.getServerUrl();
    provider.clearCapturedAuthUrl();
    await ensureCimdClientRegistration({
      serverUrl,
      provider,
      fetchFn: this.params.effectiveAuthFetch
    });
    const result = await mcpAuth(provider, {
      serverUrl,
      scope: provider.scope,
      fetchFn: this.params.effectiveAuthFetch
    });
    if (result === "AUTHORIZED") {
      throw new Error(
        "Unexpected: auth() returned AUTHORIZED without authorization code"
      );
    }
    const capturedUrl = provider.getCapturedAuthUrl();
    if (!capturedUrl) {
      throw new Error("Failed to capture authorization URL");
    }
    const stateParam = capturedUrl.searchParams.get("state");
    if (stateParam && this.params.onBeforeOAuthRedirect) {
      const parsedState = parseOAuthState(stateParam);
      if (parsedState?.authId) {
        await this.params.onBeforeOAuthRedirect(parsedState.authId);
      }
    }
    const clientInfo = await provider.clientInformation();
    this.oauthFlowState = {
      ...EMPTY_OAUTH_FLOW_STATE,
      oauthStep: "authorization_code",
      authorizationUrl: capturedUrl,
      oauthClientInfo: clientInfo ?? null
    };
    return capturedUrl;
  }
  async completeOAuthFlow(authorizationCode, iss) {
    try {
      if (this.isEnterpriseManaged()) {
        const emaConfig = await this.getEmaFlowConfig();
        const scopeForMint = this.pendingAuthorizationScope ?? emaConfig.scope;
        const config = scopeForMint ? { ...emaConfig, scope: scopeForMint } : emaConfig;
        const tokens2 = await completeEmaIdpAuthorizationAndMint(
          config,
          authorizationCode,
          iss
        );
        const requestedScope2 = this.pendingAuthorizationScope;
        const scopeToPersist2 = resolvePersistedScopeAfterGrant(
          tokens2.scope,
          requestedScope2
        );
        if (scopeToPersist2) {
          await this.requireStorage().saveScope(
            this.getServerUrl(),
            scopeToPersist2
          );
        }
        this.pendingAuthorizationScope = void 0;
        const completedAt2 = Date.now();
        this.oauthFlowState = {
          ...EMPTY_OAUTH_FLOW_STATE,
          oauthStep: "complete",
          oauthTokens: tokens2,
          completedAt: completedAt2
        };
        this.params.dispatchOAuthComplete({ tokens: tokens2 });
        return;
      }
      const provider = await this.createOAuthProvider();
      const serverUrl = this.getServerUrl();
      const result = await mcpAuth(provider, {
        serverUrl,
        authorizationCode,
        iss,
        fetchFn: this.params.effectiveAuthFetch
      });
      if (result !== "AUTHORIZED") {
        throw new Error(
          `Expected AUTHORIZED after providing authorization code, got: ${result}`
        );
      }
      const tokens = await provider.tokens();
      if (!tokens) {
        throw new Error("Failed to retrieve tokens after authorization");
      }
      const requestedScope = this.pendingAuthorizationScope;
      const scopeToPersist = resolvePersistedScopeAfterGrant(
        tokens.scope,
        requestedScope
      );
      if (scopeToPersist) {
        await provider.saveScope(scopeToPersist);
      }
      this.pendingAuthorizationScope = void 0;
      const clientInfo = await provider.clientInformation();
      const completedAt = Date.now();
      this.oauthFlowState = this.oauthFlowState ? {
        ...this.oauthFlowState,
        oauthStep: "complete",
        oauthTokens: tokens,
        oauthClientInfo: clientInfo ?? null,
        completedAt
      } : {
        ...EMPTY_OAUTH_FLOW_STATE,
        oauthStep: "complete",
        oauthTokens: tokens,
        oauthClientInfo: clientInfo ?? null,
        completedAt
      };
      this.params.dispatchOAuthComplete({ tokens });
    } catch (error) {
      this.pendingAuthorizationScope = void 0;
      this.params.dispatchOAuthError({
        error: error instanceof Error ? error : new Error(String(error))
      });
      throw error;
    }
  }
  async getOAuthTokens() {
    if (this.oauthFlowState?.oauthTokens) {
      return this.oauthFlowState.oauthTokens;
    }
    const provider = await this.createOAuthProvider();
    try {
      return await provider.tokens();
    } catch {
      return void 0;
    }
  }
  async clearOAuthTokens() {
    if (!this.oauthConfig?.storage) {
      return;
    }
    const serverUrl = this.getServerUrl();
    await this.oauthConfig.storage.clear(serverUrl);
    this.oauthFlowState = null;
    this.pendingAuthorizationScope = void 0;
  }
  async isOAuthAuthorized() {
    const tokens = await this.getOAuthTokens();
    return tokens !== void 0;
  }
  getOAuthFlowState() {
    return this.oauthFlowState ? { ...this.oauthFlowState } : void 0;
  }
  getOAuthFlowStep() {
    return this.oauthFlowState?.oauthStep;
  }
  /**
   * Persisted OAuth authorization snapshot for this server (storage + config).
   * Returns undefined when OAuth is not configured for the server.
   */
  async getOAuthState() {
    const storage = this.oauthConfig.storage;
    if (!storage) {
      return void 0;
    }
    const serverUrl = this.getServerUrl();
    const hasConfiguredOptions = isServerOAuthConfigured(this.oauthConfig);
    if (!hasConfiguredOptions && !await hasPersistedOAuthServerState(storage, serverUrl)) {
      return void 0;
    }
    return buildOAuthConnectionState({
      serverUrl: this.getServerUrl(),
      protocol: protocolFromOAuthConfig(this.oauthConfig),
      configuredScope: this.oauthConfig.scope,
      enterpriseManagedAuth: this.params.enterpriseManagedAuth,
      storage,
      flowState: this.oauthFlowState ?? void 0
    });
  }
  /**
   * Re-run EMA legs 2–3 when resource tokens expire but IdP session remains valid.
   */
  async refreshEnterpriseManagedTokens() {
    if (!this.isEnterpriseManaged()) return false;
    const tokens = await refreshEmaResourceTokens(
      await this.getEmaFlowConfig()
    );
    return tokens !== void 0;
  }
  /**
   * Re-read persisted OAuth state and determine whether `challenge` is already
   * satisfied without an authorization-server round-trip.
   *
   * Returns `true` for `insufficient_scope` when stored + token scope cover the
   * SEP-2350 union. For `token_expired`, returns `true` when a usable access
   * token is already in storage. `invalid_token` and `unauthorized` always
   * return `false` — the resource server explicitly rejected the credential.
   */
  async checkAuthChallengeSatisfied(challenge) {
    const storage = this.oauthConfig.storage;
    if (!storage) {
      return false;
    }
    const serverUrl = this.getServerUrl();
    const tokens = await storage.getTokens(serverUrl);
    if (!tokens?.access_token) {
      return false;
    }
    if (challenge.reason !== "insufficient_scope") {
      return challenge.reason === "token_expired" && isAccessTokenUsable(tokens);
    }
    const enriched = await this.enrichChallengeWithAuthorizationScopes(
      challenge,
      tokens.scope
    );
    const authorizationScopesJoined = enriched.authorizationScopes?.join(" ");
    const scopeForAuth = authorizationScopesJoined ?? enriched.requiredScopes?.join(" ");
    if (!scopeForAuth?.trim()) {
      return false;
    }
    const effectiveScope = resolveEffectiveGrantedScope(
      await storage.getScope(serverUrl),
      tokens.scope
    );
    return !isStrictScopeSuperset(scopeForAuth, effectiveScope);
  }
  /**
   * Satisfy a mid-session auth challenge when possible (silent refresh/re-mint or
   * interactive redirect).
   *
   * Only `insufficient_scope` short-circuits on {@link checkAuthChallengeSatisfied}
   * here — `token_expired` / `unauthorized` still attempt silent refresh even when
   * storage holds a locally-valid token (the resource server may have invalidated it).
   * Callers use {@link checkAuthChallengeSatisfied} directly before visible OAuth.
   *
   * Recovery is serialized per server so parallel challenges cannot race on
   * `pendingAuthorizationScope` or OAuth provider state.
   */
  async handleAuthChallenge(challenge, options) {
    if (challenge.reason === "insufficient_scope" && await this.checkAuthChallengeSatisfied(challenge)) {
      return { kind: "satisfied" };
    }
    const prior = this.authChallengeMutex;
    let release;
    const gate = new Promise((resolve4) => {
      release = resolve4;
    });
    this.authChallengeMutex = gate;
    await prior;
    try {
      if (challenge.reason === "insufficient_scope" && await this.checkAuthChallengeSatisfied(challenge)) {
        return { kind: "satisfied" };
      }
      return await this.runHandleAuthChallenge(challenge, options);
    } finally {
      release();
    }
  }
  async runHandleAuthChallenge(challenge, options) {
    if (this.isEnterpriseManaged()) {
      return this.handleEnterpriseManagedAuthChallenge(challenge, options);
    }
    return this.handleStandardAuthChallenge(challenge);
  }
  async enrichChallengeWithAuthorizationScopes(challenge, grantedTokenScope) {
    if (challenge.reason !== "insufficient_scope") {
      return challenge;
    }
    const storage = this.oauthConfig.storage;
    const serverUrl = this.getServerUrl();
    if (grantedTokenScope === void 0 && storage) {
      const tokens = await storage.getTokens(serverUrl);
      grantedTokenScope = tokens?.scope;
    }
    const previousScope = computeScopeUnion(
      storage ? await storage.getScope(serverUrl) : void 0,
      grantedTokenScope
    );
    const requiredFromChallenge = challenge.requiredScopes?.filter(Boolean) ?? [];
    const grantedSet = new Set(parseScopeString(previousScope));
    const missingRequired = requiredFromChallenge.filter(
      (scope) => !grantedSet.has(scope)
    );
    const requiredScopes = missingRequired.length > 0 ? missingRequired : requiredFromChallenge;
    const authorizationScopes = unionAuthorizationScopes(
      previousScope,
      requiredFromChallenge
    );
    return {
      ...challenge,
      requiredScopes,
      authorizationScopes
    };
  }
  resolveEmaScopeForChallenge(challenge) {
    if (challenge.reason === "insufficient_scope") {
      const fromChallenge = challenge.requiredScopes?.join(" ").trim();
      if (fromChallenge) {
        return fromChallenge;
      }
    }
    return this.oauthConfig.scope?.trim() || void 0;
  }
  async emaFlowConfigForChallenge(challenge) {
    const base = await this.getEmaFlowConfig();
    const enriched = challenge.authorizationScopes;
    if (enriched && enriched.length > 0) {
      return { ...base, scope: enriched.join(" ") };
    }
    const scope = this.resolveEmaScopeForChallenge(challenge);
    return scope ? { ...base, scope } : base;
  }
  async handleEnterpriseManagedAuthChallenge(challenge, options) {
    const enriched = await this.enrichChallengeWithAuthorizationScopes(challenge);
    if (enriched.reason === "insufficient_scope" && !options?.confirmedStepUp) {
      return { kind: "step_up_confirm", challenge: enriched };
    }
    const config = await this.emaFlowConfigForChallenge(enriched);
    if (enriched.reason === "insufficient_scope") {
      const silent = await trySilentEmaAuth(config);
      if (silent.status === "success") {
        if (await this.checkAuthChallengeSatisfied(enriched)) {
          const minted = await this.requireStorage().getTokens(
            this.getServerUrl()
          );
          const scopeToPersist = resolvePersistedScopeAfterGrant(
            minted?.scope,
            enriched.authorizationScopes?.join(" ")
          );
          if (scopeToPersist) {
            await this.requireStorage().saveScope(
              this.getServerUrl(),
              scopeToPersist
            );
          }
          return { kind: "satisfied" };
        }
      }
      if (silent.status === "mint_failed") {
        return { kind: "failed", error: silent.error };
      }
    } else {
      const tokens = await refreshEmaResourceTokens(config);
      if (tokens) {
        return { kind: "satisfied" };
      }
    }
    try {
      const authorizationUrl = await startEmaIdpAuthorization(config);
      if (enriched.reason === "insufficient_scope" && enriched.authorizationScopes?.length) {
        this.pendingAuthorizationScope = enriched.authorizationScopes.join(" ");
      }
      return { kind: "interactive", authorizationUrl, challenge: enriched };
    } catch (error) {
      return {
        kind: "failed",
        error: error instanceof Error ? error : new Error(String(error))
      };
    }
  }
  async handleStandardAuthChallenge(challenge) {
    const provider = await this.createOAuthProvider();
    const serverUrl = this.getServerUrl();
    const tokens = await provider.tokens();
    const enriched = await this.enrichChallengeWithAuthorizationScopes(
      challenge,
      tokens?.scope
    );
    provider.clearCapturedAuthUrl();
    await ensureCimdClientRegistration({
      serverUrl,
      provider,
      fetchFn: this.params.effectiveAuthFetch
    });
    const scopeForAuth = enriched.reason === "insufficient_scope" ? enriched.authorizationScopes?.join(" ") : this.oauthConfig.scope?.trim() || (enriched.requiredScopes?.length ? enriched.requiredScopes.join(" ") : void 0);
    provider.setSuppressAuthorizationNavigation(true);
    let result;
    try {
      result = await mcpAuth(provider, {
        serverUrl,
        scope: scopeForAuth,
        fetchFn: this.params.effectiveAuthFetch,
        ...enriched.reason === "insufficient_scope" && {
          forceReauthorization: isStrictScopeSuperset(
            scopeForAuth,
            tokens?.scope
          )
        }
      });
    } finally {
      provider.setSuppressAuthorizationNavigation(false);
    }
    if (result === "AUTHORIZED") {
      if (enriched.reason === "insufficient_scope") {
        if (await this.checkAuthChallengeSatisfied(enriched)) {
          const freshTokens = await provider.tokens();
          const scopeToPersist = resolvePersistedScopeAfterGrant(
            freshTokens?.scope,
            scopeForAuth
          );
          if (scopeToPersist) {
            await provider.saveScope(scopeToPersist);
          }
          return { kind: "satisfied" };
        }
        const forced = await this.tryForceReauthorizationForStepUp(
          provider,
          serverUrl,
          scopeForAuth,
          enriched
        );
        if (forced) {
          return forced;
        }
        return {
          kind: "failed",
          error: new Error(stepUpInsufficientScopeMessage(enriched))
        };
      } else {
        return { kind: "satisfied" };
      }
    }
    const capturedUrl = provider.getCapturedAuthUrl();
    if (!capturedUrl) {
      return {
        kind: "failed",
        error: new Error("Failed to capture authorization URL")
      };
    }
    if (enriched.reason === "insufficient_scope" && scopeForAuth) {
      this.pendingAuthorizationScope = scopeForAuth;
    }
    const clientInfo = await provider.clientInformation();
    await this.recordAuthorizationCodeFlowState(
      capturedUrl,
      clientInfo,
      // SEP-2350: label a step-up redirect distinctly from a first-time login.
      enriched.reason === "insufficient_scope" ? "scope_step_up" : "authorization_code"
    );
    return {
      kind: "interactive",
      authorizationUrl: capturedUrl,
      challenge: enriched
    };
  }
  /** Start interactive OAuth after handleAuthChallenge returns `interactive`. */
  async beginInteractiveAuthorization(authorizationUrl) {
    const stateParam = authorizationUrl.searchParams.get("state");
    if (stateParam && this.params.onBeforeOAuthRedirect) {
      const parsedState = parseOAuthState(stateParam);
      if (parsedState?.authId) {
        await this.params.onBeforeOAuthRedirect(parsedState.authId);
      }
    }
    this.requireNavigation().navigateToAuthorization(authorizationUrl);
    const provider = await this.createOAuthProvider();
    const clientInfo = await provider.clientInformation();
    await this.recordAuthorizationCodeFlowState(authorizationUrl, clientInfo);
    this.params.dispatchOAuthAuthorizationRequired({ url: authorizationUrl });
  }
  requireNavigation() {
    const navigation = this.oauthConfig.navigation;
    if (!navigation) {
      throw new Error("OAuth navigation is required.");
    }
    return navigation;
  }
  async recordAuthorizationCodeFlowState(authorizationUrl, oauthClientInfo, step = "authorization_code") {
    this.oauthFlowState = {
      ...EMPTY_OAUTH_FLOW_STATE,
      oauthStep: step,
      authorizationUrl,
      oauthClientInfo: oauthClientInfo ?? null
    };
  }
  /**
   * Silent refresh returned AUTHORIZED but token scope still lacks the step-up
   * union — force a fresh authorization redirect.
   */
  async tryForceReauthorizationForStepUp(provider, serverUrl, scopeForAuth, enriched) {
    provider.clearCapturedAuthUrl();
    provider.setSuppressAuthorizationNavigation(true);
    let result;
    try {
      result = await mcpAuth(provider, {
        serverUrl,
        scope: scopeForAuth,
        fetchFn: this.params.effectiveAuthFetch,
        forceReauthorization: true
      });
    } finally {
      provider.setSuppressAuthorizationNavigation(false);
    }
    if (result !== "AUTHORIZED") {
      return null;
    }
    if (await this.checkAuthChallengeSatisfied(enriched)) {
      const freshTokens = await provider.tokens();
      const scopeToPersist = resolvePersistedScopeAfterGrant(
        freshTokens?.scope,
        scopeForAuth
      );
      if (scopeToPersist) {
        await provider.saveScope(scopeToPersist);
      }
      return { kind: "satisfied" };
    }
    return null;
  }
  /**
   * Create an OAuth provider for transport auth (connect()).
   * Used only when isHttpOAuthConfig() is true.
   */
  async createOAuthProviderForTransport() {
    const provider = await this.createOAuthProvider();
    if (this.isEnterpriseManaged()) {
      return new EmaTransportOAuthProvider(
        provider,
        await this.getEmaFlowConfig()
      );
    }
    return provider;
  }
};

// ../../core/mcp/remote/types.ts
function oauthTokensToRemoteAuthState(tokens) {
  return {
    oauthTokens: {
      access_token: tokens.access_token,
      token_type: tokens.token_type,
      expires_in: tokens.expires_in,
      refresh_token: tokens.refresh_token,
      scope: tokens.scope,
      id_token: tokens.id_token
    }
  };
}

// ../../core/mcp/remote/remoteClientTransport.ts
var DEFAULT_SSE_RESPONSE_TIMEOUT_MS = 6e4;
function requestIdForMessage(message) {
  if ("method" in message && "id" in message && message.id !== null && message.id !== void 0) {
    if (message.method === "subscriptions/listen") {
      return void 0;
    }
    return message.id;
  }
  return void 0;
}
function isConnectAuthChallenge(json) {
  return typeof json === "object" && json !== null && "ok" in json && json.ok === false && "kind" in json && json.kind === "auth_challenge";
}
function isConnectTransportError(json) {
  return typeof json === "object" && json !== null && "ok" in json && json.ok === false && "kind" in json && json.kind === "transport_error";
}
function legacySessionId(json) {
  if (typeof json === "object" && json !== null && "sessionId" in json && typeof json.sessionId === "string" && !("ok" in json)) {
    return json.sessionId;
  }
  if (typeof json === "object" && json !== null && "ok" in json && json.ok === true && "sessionId" in json) {
    return json.sessionId;
  }
  return void 0;
}
async function* parseSSE(reader) {
  const decoder = new TextDecoder();
  let buffer = "";
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop() ?? "";
    let currentEvent = "message";
    let currentData = [];
    for (const line of lines) {
      if (line.startsWith("event:")) {
        currentEvent = line.slice(6).trim();
      } else if (line.startsWith("data:")) {
        currentData.push(line.slice(5).trimStart());
      } else if (line === "") {
        if (currentData.length > 0) {
          yield { event: currentEvent, data: currentData.join("\n") };
        }
        currentEvent = "message";
        currentData = [];
      }
    }
  }
  if (buffer.trim()) {
    const lines = buffer.split("\n");
    let currentEvent = "message";
    const currentData = [];
    for (const line of lines) {
      if (line.startsWith("event:")) currentEvent = line.slice(6).trim();
      else if (line.startsWith("data:"))
        currentData.push(line.slice(5).trimStart());
    }
    if (currentData.length > 0) {
      yield { event: currentEvent, data: currentData.join("\n") };
    }
  }
}
var RemoteClientTransport = class {
  _sessionId = void 0;
  _protocolVersion = void 0;
  eventStreamReader = null;
  eventStreamAbort = null;
  eventStreamConsumeTask = null;
  restartingEventStream = false;
  closed = false;
  sseResponseWaits = /* @__PURE__ */ new Map();
  options;
  config;
  /**
   * Intentionally returns undefined. The MCP Client checks transport.sessionId to detect
   * reconnects and skip initialize. Our _sessionId is the remote server's session ID, not
   * the MCP protocol's initialization state. Exposing it would cause the MCP Client to
   * skip initialize and send tools/list first, which fails on streamable-http (and any
   * transport requiring initialize before other requests).
   */
  get sessionId() {
    return void 0;
  }
  /** Remote Hono session id (distinct from MCP protocol session). */
  getRemoteBackendSessionId() {
    return this._sessionId;
  }
  /**
   * The SDK Client calls this with the negotiated protocol version as soon as
   * `initialize` resolves — before it sends `notifications/initialized` — so an
   * HTTP transport can stamp `Mcp-Protocol-Version` on every later request.
   *
   * Here the real upstream transport lives on the backend, so we can only
   * record the version and forward it on the next `/api/mcp/send`, which
   * applies it to the upstream transport *before* sending. Because
   * `notifications/initialized` is that next send, it and everything after it
   * (including the standalone SSE GET and the session DELETE, both issued by
   * the upstream transport itself) carry the header (#1935).
   */
  setProtocolVersion(version) {
    this._protocolVersion = version;
  }
  /**
   * Reattach to an existing remote backend session after a full-page OAuth
   * redirect. Opens the SSE event stream without POST /connect.
   */
  async attachToSession(sessionId) {
    if (this.closed) {
      this.closed = false;
    }
    await this.stopEventStream();
    this._sessionId = sessionId;
    await this.openEventStream();
  }
  constructor(options, config) {
    this.options = options;
    this.config = config;
  }
  setAuthRecovery(handlers) {
    this.options.authRecovery = handlers;
  }
  setOnAuthChallenge(handler) {
    this.options.onAuthChallenge = handler;
  }
  get fetchFn() {
    return this.options.fetchFn ?? globalThis.fetch;
  }
  get baseUrl() {
    return this.options.baseUrl.replace(/\/$/, "");
  }
  get headers() {
    const h = {
      "Content-Type": "application/json"
    };
    if (this.options.authToken) {
      h["x-mcp-remote-auth"] = `Bearer ${this.options.authToken}`;
    }
    return h;
  }
  async recoverFromAuthChallenge(challenge, retry) {
    const recovery = this.options.authRecovery;
    if (!recovery) {
      throw new AuthChallengeError(challenge, challenge.raw?.httpStatus ?? 401);
    }
    const outcome = await recovery.handleAuthChallenge(challenge);
    if (outcome.kind === "satisfied") {
      await retry();
      return;
    }
    if (outcome.kind === "step_up_confirm") {
      throw new AuthRecoveryRequiredError(
        EMA_STEP_UP_PENDING_URL,
        outcome.challenge,
        { emaStepUpConfirm: true }
      );
    }
    if (outcome.kind === "interactive") {
      throw new AuthRecoveryRequiredError(
        outcome.authorizationUrl,
        outcome.challenge
      );
    }
    throw outcome.error;
  }
  async start() {
    if (this._sessionId && !this.closed) {
      return;
    }
    return this.startWithRecovery(0);
  }
  async startWithRecovery(retryCount) {
    if (this.sessionId) return;
    if (this.closed) throw new Error("Transport is closed");
    let authState;
    if (this.options.authProvider) {
      const tokens = await this.options.authProvider.tokens();
      if (tokens) {
        authState = oauthTokensToRemoteAuthState(tokens);
      }
    }
    const body = {
      config: this.config,
      ...authState && { authState },
      ...this.options.settings && { settings: this.options.settings }
    };
    const res = await this.fetchFn(`${this.baseUrl}/api/mcp/connect`, {
      method: "POST",
      headers: this.headers,
      body: JSON.stringify(body)
    });
    if (!res.ok) {
      const text = await res.text();
      const error = new Error(`Remote connect failed (${res.status}): ${text}`);
      error.status = res.status;
      throw error;
    }
    const json = await res.json();
    if (isConnectAuthChallenge(json)) {
      if (retryCount >= 1) {
        throw new AuthChallengeError(
          json.authChallenge,
          json.authChallenge.raw?.httpStatus ?? 401
        );
      }
      await this.recoverFromAuthChallenge(
        json.authChallenge,
        () => this.startWithRecovery(retryCount + 1)
      );
      return;
    }
    if (isConnectTransportError(json)) {
      throw new Error(`Remote connect failed: ${json.error}`);
    }
    const sessionId = legacySessionId(json);
    if (!sessionId) {
      throw new Error("Remote did not return sessionId");
    }
    this._sessionId = sessionId;
    await this.openEventStream();
  }
  async openEventStream() {
    this.eventStreamAbort = new AbortController();
    const eventRes = await this.fetchFn(
      `${this.baseUrl}/api/mcp/events?sessionId=${encodeURIComponent(this._sessionId)}`,
      {
        headers: this.options.authToken ? { "x-mcp-remote-auth": `Bearer ${this.options.authToken}` } : {},
        signal: this.eventStreamAbort.signal
      }
    );
    if (!eventRes.ok) {
      this._sessionId = void 0;
      throw new Error(
        `Remote events stream failed (${eventRes.status}): ${await eventRes.text()}`
      );
    }
    const bodyStream = eventRes.body;
    if (!bodyStream) {
      throw new Error("Remote events stream has no body");
    }
    this.eventStreamReader = bodyStream.getReader();
    this.eventStreamConsumeTask = this.consumeEventStream();
  }
  /** Stop the SSE consumer and release the reader before opening a new stream. */
  async stopEventStream() {
    this.restartingEventStream = true;
    try {
      this.eventStreamAbort?.abort();
      const reader = this.eventStreamReader;
      if (reader) {
        try {
          await reader.cancel();
        } catch {
        }
      }
      this.eventStreamReader = null;
      if (this.eventStreamConsumeTask) {
        await this.eventStreamConsumeTask;
        this.eventStreamConsumeTask = null;
      }
    } finally {
      this.restartingEventStream = false;
    }
  }
  async consumeEventStream() {
    if (!this.eventStreamReader) return;
    try {
      for await (const { data } of parseSSE(this.eventStreamReader)) {
        if (this.closed) break;
        try {
          const parsed = JSON.parse(data);
          if (parsed.type === "message") {
            const msg = parsed.data;
            this.settleSseResponseWait(msg);
            this.onmessage?.(msg, void 0);
          } else if (parsed.type === "fetch_request" && this.options.onFetchRequest) {
            const entry = parsed.data;
            this.options.onFetchRequest({
              ...entry,
              timestamp: typeof entry.timestamp === "string" ? new Date(entry.timestamp) : entry.timestamp
            });
          } else if (parsed.type === "fetch_request_body_update" && this.options.onFetchResponseBody) {
            this.options.onFetchResponseBody(
              parsed.data.id,
              parsed.data.responseBody
            );
          } else if (parsed.type === "stdio_log" && this.options.onStderr) {
            this.options.onStderr({
              timestamp: new Date(parsed.data.timestamp),
              message: parsed.data.message
            });
          } else if (parsed.type === "auth_challenge") {
            this.options.onAuthChallenge?.(parsed.data);
          } else if (parsed.type === "transport_error") {
            const error = new Error(parsed.data.error);
            if (parsed.data.code !== void 0) {
              error.code = parsed.data.code;
            }
            if (!this.restartingEventStream) {
              this.onerror?.(error);
              if (!this.closed) {
                this.closed = true;
                this.onclose?.();
              }
            }
          }
        } catch (err) {
          this.onerror?.(err instanceof Error ? err : new Error(String(err)));
        }
      }
    } catch (err) {
      if (!this.closed && err instanceof Error && err.name !== "AbortError") {
        this.onerror?.(err);
      }
    } finally {
      this.eventStreamReader = null;
      if (!this.closed && !this.restartingEventStream) {
        this.closed = true;
        this.onclose?.();
      }
    }
  }
  async send(message, options) {
    return this.postSend(message, options, 0);
  }
  /**
   * Push auth state to the remote backend without tearing down the session.
   * Used after mid-session OAuth recovery in the web client.
   */
  async pushAuthState(authState) {
    if (!this._sessionId) {
      throw new Error("Transport not started");
    }
    if (this.closed) {
      throw new Error("Transport is closed");
    }
    const state = authState ?? await this.buildAuthStateFromProvider();
    if (!state.oauthTokens && !state.oauthClient) {
      throw new Error("No auth state to push");
    }
    const res = await this.fetchFn(`${this.baseUrl}/api/mcp/auth-state`, {
      method: "POST",
      headers: this.headers,
      body: JSON.stringify({ sessionId: this._sessionId, authState: state })
    });
    if (!res.ok) {
      const text = await res.text();
      throw new Error(
        `Remote auth-state update failed (${res.status}): ${text}`
      );
    }
    const json = await res.json();
    if (!json.ok) {
      throw new Error(json.error ?? "Remote auth-state update failed");
    }
  }
  async buildAuthStateFromProvider() {
    if (!this.options.authProvider) {
      throw new Error("No auth provider configured");
    }
    const tokens = await this.options.authProvider.tokens();
    if (!tokens) {
      throw new Error("No OAuth tokens available");
    }
    return oauthTokensToRemoteAuthState(tokens);
  }
  get sseResponseTimeoutMs() {
    return this.options.sseResponseTimeoutMs ?? DEFAULT_SSE_RESPONSE_TIMEOUT_MS;
  }
  settleSseResponseWait(message) {
    if (!("id" in message) || message.id === null || message.id === void 0 || !("result" in message) && !("error" in message)) {
      return;
    }
    const wait = this.sseResponseWaits.get(message.id);
    if (!wait) {
      return;
    }
    this.sseResponseWaits.delete(message.id);
    wait.resolve();
  }
  cancelSseResponseWait(requestId) {
    const wait = this.sseResponseWaits.get(requestId);
    if (!wait) {
      return;
    }
    this.sseResponseWaits.delete(requestId);
    wait.reject(new Error("SSE response wait cancelled"));
  }
  cancelAllSseWaits(error) {
    for (const wait of this.sseResponseWaits.values()) {
      wait.reject(error);
    }
    this.sseResponseWaits.clear();
  }
  waitForSseResponse(requestId) {
    return new Promise((resolve4, reject) => {
      const timer = setTimeout(() => {
        this.sseResponseWaits.delete(requestId);
        reject(
          new Error(
            `Timed out waiting for MCP response on SSE (${this.sseResponseTimeoutMs}ms)`
          )
        );
      }, this.sseResponseTimeoutMs);
      this.sseResponseWaits.set(requestId, {
        resolve: () => {
          clearTimeout(timer);
          resolve4();
        },
        reject: (error) => {
          clearTimeout(timer);
          reject(error);
        }
      });
    });
  }
  async postSend(message, options, retryCount) {
    const requestId = requestIdForMessage(message);
    let sseWait;
    if (requestId !== void 0) {
      sseWait = this.waitForSseResponse(requestId);
      void sseWait.catch(() => {
      });
    }
    let json;
    try {
      json = await this.requestSend(message, options);
    } catch (error) {
      if (requestId !== void 0) {
        this.cancelSseResponseWait(requestId);
      }
      throw error;
    }
    if (json.ok) {
      if (sseWait) {
        await sseWait;
      }
      return;
    }
    if (requestId !== void 0) {
      this.cancelSseResponseWait(requestId);
    }
    if (json.kind === "auth_challenge") {
      if (retryCount >= 1 || !this.options.authRecovery?.pushAuthState) {
        throw new AuthChallengeError(
          json.authChallenge,
          json.authChallenge.raw?.httpStatus ?? 401
        );
      }
      await this.recoverFromAuthChallenge(json.authChallenge, async () => {
        await this.options.authRecovery.pushAuthState();
        return this.postSend(message, options, retryCount + 1);
      });
      return;
    }
    throw new Error(json.error);
  }
  async requestSend(message, options) {
    if (!this._sessionId) {
      throw new Error("Transport not started");
    }
    if (this.closed) {
      throw new Error("Transport is closed");
    }
    const body = {
      sessionId: this._sessionId,
      message,
      ...options?.relatedRequestId != null && {
        relatedRequestId: options.relatedRequestId
      },
      // Forward per-send `Mcp-Param-*` headers (SEP-2243) for the backend to
      // apply to the upstream send; the browser can't set them cross-origin.
      ...options?.headers != null && { headers: options.headers },
      // Forward the negotiated protocol version so the backend's transport can
      // stamp `Mcp-Protocol-Version` on this and every later request (#1935).
      ...this._protocolVersion !== void 0 && {
        protocolVersion: this._protocolVersion
      }
    };
    const res = await this.fetchFn(`${this.baseUrl}/api/mcp/send`, {
      method: "POST",
      headers: this.headers,
      body: JSON.stringify(body)
    });
    if (!res.ok) {
      const text = await res.text();
      const error = new Error(`Remote send failed (${res.status}): ${text}`);
      error.status = res.status;
      throw error;
    }
    return await res.json();
  }
  async close() {
    if (this.closed) return;
    this.closed = true;
    this.cancelAllSseWaits(new Error("Transport closed"));
    await this.stopEventStream();
    if (this._sessionId) {
      try {
        await this.fetchFn(`${this.baseUrl}/api/mcp/disconnect`, {
          method: "POST",
          headers: this.headers,
          body: JSON.stringify({ sessionId: this._sessionId })
        });
      } catch {
      }
      this._sessionId = void 0;
    }
    this.onclose?.();
  }
  onclose;
  onerror;
  onmessage;
};

// ../../core/mcp/inspectorClient.ts
var corePackageJson = {
  name: "mcp-inspector",
  version: "0.0.0"
};
var MAX_URL_ELICITATION_RETRIES = 5;
function createPendingAbortError() {
  return new Error("Pending request aborted");
}
var TOOL_CALL_CANCELLED_REASON = "Tool call cancelled by user";
var DEFAULT_TASK_POLL_INTERVAL_MS = 500;
async function closeSubscriptionBestEffort(subscription) {
  try {
    await subscription.close();
  } catch {
  }
}
function notificationMethodFromSchema(schema) {
  if (schema !== null && typeof schema === "object") {
    const literal = schema.shape?.method?.value;
    if (typeof literal === "string") return literal;
  }
  return void 0;
}
var MODERN_RECONNECT_BASE_MS = 500;
var MODERN_RECONNECT_MAX_MS = 15e3;
var MODERN_RECONNECT_MAX_ATTEMPTS = 8;
var InspectorClient = class _InspectorClient extends InspectorClientEventTarget {
  /**
   * Upper bound on MRTR (`input_required`) rounds for a single logical request
   * before {@link requestWithInputRequired} gives up. We drive the loop
   * ourselves (`inputRequired: { autoFulfill: false }`), so this is the manual
   * counterpart to the SDK auto-driver's default `maxRounds` (10) and guards
   * against a server that keeps returning `input_required` forever.
   */
  static MRTR_MAX_ROUNDS = 10;
  client = null;
  appRendererClientProxy = null;
  // Lazily-built validator used only on the skipOutputValidation path to detect
  // (non-fatally) when a delivered result violates the tool's outputSchema.
  outputValidator = null;
  transport = null;
  baseTransport = null;
  // Correlation for `markResponseRejected` (#1953): the method of each
  // outbound request still awaiting a response, and — once one is answered —
  // the id of the most recently answered request per method. Entries are
  // dropped as responses arrive, so this holds at most one id per method
  // rather than growing with the session.
  outboundRequestMethods = /* @__PURE__ */ new Map();
  lastAnsweredRequestByMethod = /* @__PURE__ */ new Map();
  /** True when the cached transport was built with an OAuth authProvider attached. */
  transportHasAuthProvider = false;
  /** Dedupes concurrent ambient auth challenges (reason + scopes). */
  ambientAuthChallengeInFlight = /* @__PURE__ */ new Map();
  pipeStderr;
  initialLoggingLevel;
  // Modern-era per-request log level (#1629). On 2026-07-28 servers
  // `logging/setLevel` is gone; the client opts into logs per request via the
  // `io.modelcontextprotocol/logLevel` `_meta` key, and the SDK does not attach
  // it automatically. When set, `mergeMeta` stamps this level on every outgoing
  // request so server logs arrive on each request's stream; `undefined` means
  // "don't opt in" (logs stay silently absent). Only honored on the modern era.
  modernLogLevel;
  sample;
  elicit;
  progress;
  resetTimeoutOnProgress;
  requestTimeout;
  defaultMetadata;
  serverSettings;
  versionNegotiation;
  status = "disconnected";
  // True only while an explicit disconnect() owns the teardown. close() can
  // trigger the transport's onclose synchronously, so this lets onclose defer
  // the canonical status set + `disconnect` event to disconnect() and fire it
  // exactly once (see onclose / disconnect()).
  disconnecting = false;
  // Server data (resources, resourceTemplates, prompts are in state managers)
  capabilities;
  serverInfo;
  instructions;
  protocolVersion;
  // Era model (SEP §7.8). Populated after connect from the SDK Client's
  // negotiation accessors. `protocolEra` is the negotiated era ("legacy" |
  // "modern"); `discoverResult` is the `server/discover` payload on a
  // probed/pinned connection (undefined on a plain legacy connect).
  protocolEra;
  discoverResult;
  // Tools the SDK excludes from `tools/list` for invalid `x-mcp-header`
  // annotations (SEP-2243), recomputed on every aggregate tools refresh and
  // surfaced so the Tools tab can show why a tool vanished (#1632).
  excludedTools = [];
  // The capabilities this Inspector client advertises to the server during the
  // initialize handshake. Built once in setupClient() and snapshotted here so
  // UI surfaces (Server Info modal) can display them without poking at the
  // SDK Client's private state.
  clientCapabilities = {};
  // The client identity ({name, version}) passed to the SDK Client. Reused to
  // build the modern per-request envelope for raw tasks/* requests.
  clientInfo;
  // Sampling requests
  pendingSamples = [];
  // Elicitation requests
  pendingElicitations = [];
  // Roots (undefined means roots capability not enabled, empty array means enabled but no roots)
  roots;
  /**
   * Whether `capabilities.roots` was advertised at `initialize`, read off the
   * capability object actually sent rather than re-derived from the constructor
   * option. Fixed for the client's lifetime, because the capability is
   * negotiated at construction and the SDK refuses `registerCapabilities` after
   * connect.
   *
   * The `roots/list` registration gates on *this*, not on `this.roots`, which
   * `setRoots()` can make defined later: the SDK throws "Client does not support
   * roots capability" from `setRequestHandler` when the capability was never
   * advertised, and that throw would land before the handshake on every
   * subsequent `connect()` — wedging the client permanently (#1797).
   */
  rootsCapabilityAdvertised;
  /**
   * Whether `capabilities.elicitation` was advertised, on the same terms as
   * {@link rootsCapabilityAdvertised}. Not the same question as `this.elicit`
   * being truthy: `{}` / `{ form: false, url: false }` are valid options that
   * enable no mode, so nothing is advertised — and registering
   * `elicitation/create` anyway throws "Client does not support elicitation
   * capability" before the handshake, leaving the client unable to connect
   * at all (#1797).
   */
  elicitationCapabilityAdvertised;
  /** As above, for `capabilities.sampling`. */
  samplingCapabilityAdvertised;
  /** As above, for `capabilities.tasks` (the receiver-side `tasks/*` polls). */
  tasksCapabilityAdvertised;
  /** As above, for `capabilities.elicitation.url` (the URL-mode completion). */
  urlElicitationCapabilityAdvertised;
  /**
   * As above, for `capabilities.roots.listChanged` — the predicate the SDK
   * asserts before letting us *send* `roots/list_changed`, which is narrower
   * than the `capabilities.roots` presence it asserts before letting us
   * *register* `roots/list`. They coincide today (roots are only ever
   * advertised as `{ listChanged: true }`), but reading each assertion's own
   * predicate is what keeps a future conditional advertisement from silently
   * un-gating one of them.
   */
  rootsListChangedCapabilityAdvertised;
  // Content cache
  // ListChanged notification configuration
  listChangedNotifications;
  // Resource subscriptions. The set of subscribed URIs is the era-agnostic
  // source of truth for the UI (the `resourceSubscriptionsChange` list). How a
  // subscription reaches the wire forks by era: legacy sends one
  // `resources/subscribe` per URI; modern (2026-07-28) has no such method — all
  // subscriptions are a filter over one long-lived `subscriptions/listen` stream
  // (#1630, SEP §7.4).
  subscribedResources = /* @__PURE__ */ new Set();
  // Modern-era listen stream backing the subscriptions above. A single
  // `McpSubscription` whose filter's `resourceSubscriptions` mirrors
  // `subscribedResources`; mutating the set re-lists (close old, open new), and
  // an unexpected `"remote"` close re-lists (reconnect-by-re-listen — the stream
  // is not resumable). `null` on the legacy era, and on the modern era whenever
  // the filter is empty — which is *not* the same as "no URI subscribed": the
  // stream also carries the list-change opt-ins, so a tools-only server opens it
  // with no subscriptions at all (#1920).
  modernSubscription = null;
  // Monotonic guard so a stale re-list/reconnect (whose `listen()` or `closed`
  // resolves after a newer refresh already started) can detect it lost the race
  // and bail without clobbering the current stream.
  modernListenGeneration = 0;
  // Last dispatched modern stream state; `active: false` on the legacy era.
  modernStreamState = INACTIVE_SUBSCRIPTION_STREAM_STATE;
  // Reconnect-by-re-listen backoff state (#1630): the count of consecutive
  // *failed* re-lists (reset to 0 on any successful acknowledgement) and the
  // pending re-listen timer.
  modernReconnectAttempts = 0;
  modernReconnectTimer;
  // Task ids the user explicitly cancelled. A cancel makes the in-flight
  // `callToolStream` reject with a generic -32603 error, which the stream's
  // error path would otherwise report as a *failed* task — flashing "failed"
  // in the UI until a refresh fetches the server's true "cancelled" state.
  // Recording the id lets that path label the terminal task "cancelled"
  // instead, so it lands in the right state immediately (#1455). Cleared on
  // disconnect.
  cancelledTaskIds = /* @__PURE__ */ new Set();
  // Per-task abort controllers for a modern task paused at `input_required`.
  // While the poll loop blocks on the pending elicitation (the modal), the tool
  // call's own abort path isn't in play — so `cancelRequestorTask` aborts this
  // controller to reject the pending request, close the modal, and let the poll
  // observe the cancellation. Keyed by taskId; created/removed by the poll loops.
  taskInputAbortControllers = /* @__PURE__ */ new Map();
  // Pending raw-wire requests (modern tasks/* — see rawWireRequest). Keyed by a
  // string JSON-RPC id we mint; the SDK Client only mints numeric ids, so ours
  // never collide with (or reach) it. Resolved by the transport's
  // consume-response hook and rejected on disconnect.
  pendingRawWireRequests = /* @__PURE__ */ new Map();
  rawWireRequestCounter = 0;
  // Abort controller for the in-flight ordinary (non-task) tool call. Aborting
  // it makes the SDK send a `notifications/cancelled` for that request (the MCP
  // cancellation flow) and reject the pending call, which `callTool` surfaces as
  // a `ToolCallCancelledError`. Undefined when no ordinary call is in flight.
  // Task-augmented calls have a server-side task and are cancelled via
  // `cancelRequestorTask` instead, so they don't use this (#1458).
  activeToolCallAbortController;
  // Receiver tasks (server-initiated: server sends createMessage/elicit with params.task, server polls us)
  receiverTasks;
  // Per-extension advertise overrides (#1738); undefined key falls back to the
  // registry default in ADVERTISABLE_EXTENSIONS.
  advertisedExtensions;
  receiverTaskTtlMs;
  receiverTaskRecords = /* @__PURE__ */ new Map();
  // OAuth support (config owned by oauthManager; client delegates and uses !!oauthManager for "is OAuth configured")
  oauthManager = null;
  logger;
  transportClientFactory;
  fetchFn;
  effectiveAuthFetch;
  // Session ID (for OAuth state and saveSession event; persistence is in FetchRequestLogState)
  sessionId;
  transportConfig;
  /** null until first transport is built; then true for in-process OAuth runners. */
  directAuthRecoveryActive = null;
  /**
   * Nesting depth of in-flight silent auth recoveries. `withDirectAuthRecovery`
   * bounds retries per call (`attempt >= 1`), but a satisfied outcome recovers
   * by running a *nested* `connect()`, which starts its own recovery with a
   * fresh counter — so a server that keeps answering 401 with freshly refreshed
   * tokens would recurse without bound. Counted across the nesting boundary and
   * capped at {@link InspectorClient.MAX_NESTED_AUTH_RECOVERIES}.
   */
  authRecoveryDepth = 0;
  /**
   * Cap on nested silent recoveries. One is the normal case (challenge →
   * refresh → reconnect); a couple more absorb a legitimately re-challenged
   * reconnect. Beyond that the credentials are not fixing the challenge, so the
   * challenge is surfaced to the caller instead of recovered again.
   */
  static MAX_NESTED_AUTH_RECOVERIES = 3;
  /**
   * Opt-in from {@link InspectorClientOptions.directAuthRecovery}: when true and
   * the live transport is direct (not {@link RemoteClientTransport}), RPCs use
   * fetch intercept + {@link withDirectAuthRecovery}.
   */
  directAuthRecovery;
  constructor(transportConfig, options) {
    super();
    this.transportConfig = transportConfig;
    this.transportClientFactory = options.environment.transport;
    this.fetchFn = options.environment.fetch;
    this.logger = options.environment.logger ?? silentLogger;
    this.pipeStderr = options.pipeStderr ?? false;
    this.initialLoggingLevel = options.initialLoggingLevel;
    this.sample = options.sample ?? true;
    this.elicit = options.elicit ?? true;
    this.receiverTasks = options.receiverTasks ?? false;
    this.advertisedExtensions = options.advertisedExtensions;
    this.receiverTaskTtlMs = options.receiverTaskTtlMs ?? 6e4;
    this.progress = options.progress ?? true;
    this.resetTimeoutOnProgress = options.resetTimeoutOnProgress ?? true;
    this.requestTimeout = options.timeout;
    this.defaultMetadata = options.defaultMetadata && Object.keys(options.defaultMetadata).length > 0 ? options.defaultMetadata : void 0;
    this.serverSettings = options.serverSettings;
    this.modernLogLevel = resolveModernLogLevel(options.serverSettings);
    this.versionNegotiation = options.versionNegotiation ?? { mode: "legacy" };
    this.directAuthRecovery = options.directAuthRecovery ?? false;
    this.roots = options.roots !== void 0 ? cleanRoots(options.roots) : void 0;
    this.listChangedNotifications = {
      tools: options.listChangedNotifications?.tools ?? true,
      resources: options.listChangedNotifications?.resources ?? true,
      prompts: options.listChangedNotifications?.prompts ?? true
    };
    this.effectiveAuthFetch = this.buildEffectiveAuthFetch();
    this.sessionId = options.sessionId;
    if (options.oauth || options.environment.oauth) {
      const oauthConfig = {
        // Environment components (storage, navigation, redirectUrlProvider)
        ...options.environment.oauth,
        // Config values (clientId, clientSecret, clientMetadataUrl, scope)
        ...options.oauth
      };
      this.oauthManager = new OAuthManager({
        getServerUrl: () => this.getServerUrl(),
        effectiveAuthFetch: this.effectiveAuthFetch,
        getEventTarget: () => this,
        onBeforeOAuthRedirect: (sessionId) => {
          this.sessionId = sessionId;
          this.saveSession();
          return Promise.resolve();
        },
        initialConfig: oauthConfig,
        enterpriseManagedAuth: options.enterpriseManagedAuth,
        installEnterpriseManagedAuth: options.installEnterpriseManagedAuth,
        dispatchOAuthComplete: (detail) => this.dispatchTypedEvent("oauthComplete", detail),
        dispatchOAuthAuthorizationRequired: (detail) => this.dispatchTypedEvent("oauthAuthorizationRequired", detail),
        dispatchOAuthError: (detail) => this.dispatchTypedEvent("oauthError", detail)
      });
    }
    const clientOptions = {
      // Per-server protocol era (SEP §7.8), threaded from config via
      // `eraToVersionNegotiation` and defaulted to `{ mode: "legacy" }` in the
      // constructor. "legacy" keeps the wire byte-identical to a 2025 client;
      // "auto"/"modern" opt into 2026-era negotiation (#1626).
      versionNegotiation: this.versionNegotiation,
      // Drive MRTR (SEP-2322) manually instead of letting the SDK auto-fulfil
      // and hide the retry loop (#1704). Unconditional and safe on every era:
      // legacy servers never return `input_required`, so this is a no-op there;
      // on modern connections the three multi-round-trip methods opt in via
      // `allowInputRequired` in `requestWithInputRequired`, and no other method
      // can receive an `input_required` result. The negotiated era is unknown
      // at construction time, so gating here is impossible anyway.
      inputRequired: { autoFulfill: false }
    };
    const capabilities = {};
    if (this.sample) {
      capabilities.sampling = {};
    }
    if (this.elicit) {
      const elicitationCap = {};
      if (this.elicit === true) {
        elicitationCap.form = {};
      } else {
        if (this.elicit.form) {
          elicitationCap.form = {};
        }
        if (this.elicit.url) {
          elicitationCap.url = {};
        }
      }
      if (Object.keys(elicitationCap).length > 0) {
        capabilities.elicitation = elicitationCap;
      }
    }
    if (this.roots !== void 0) {
      capabilities.roots = { listChanged: true };
    }
    if (this.receiverTasks) {
      const taskRequests = {};
      if (capabilities.sampling) {
        taskRequests.sampling = { createMessage: {} };
      }
      if (capabilities.elicitation) {
        taskRequests.elicitation = { create: {} };
      }
      capabilities.tasks = {
        list: {},
        cancel: {},
        ...Object.keys(taskRequests).length > 0 && { requests: taskRequests }
      };
    }
    const advertisedExtensions = buildClientExtensions({
      enterpriseManaged: options.oauth?.enterpriseManaged ?? false,
      advertised: this.advertisedExtensions
    });
    if (Object.keys(advertisedExtensions).length > 0) {
      capabilities.extensions = {
        ...capabilities.extensions,
        ...advertisedExtensions
      };
    }
    clientOptions.capabilities = capabilities;
    this.clientCapabilities = capabilities;
    this.rootsCapabilityAdvertised = capabilities.roots !== void 0;
    this.elicitationCapabilityAdvertised = capabilities.elicitation !== void 0;
    this.samplingCapabilityAdvertised = capabilities.sampling !== void 0;
    this.tasksCapabilityAdvertised = capabilities.tasks !== void 0;
    this.urlElicitationCapabilityAdvertised = capabilities.elicitation?.url !== void 0;
    this.rootsListChangedCapabilityAdvertised = capabilities.roots?.listChanged === true;
    this.appRendererClientProxy = null;
    this.clientInfo = options.clientIdentity ?? {
      name: corePackageJson.name.split("/")[1] ?? corePackageJson.name,
      version: corePackageJson.version
    };
    this.client = new Client(
      this.clientInfo,
      Object.keys(clientOptions).length > 0 ? clientOptions : void 0
    );
  }
  buildEffectiveAuthFetch() {
    const base = this.fetchFn ?? fetch;
    return createFetchTracker(base, {
      trackRequest: (entry) => this.dispatchFetchRequest({ ...entry, category: "auth" }),
      updateResponseBody: (id, body) => this.dispatchFetchRequestBodyUpdate(id, body)
    });
  }
  createMessageTrackingCallbacks() {
    return {
      trackRequest: (message, origin) => {
        if (origin === "client") {
          this.outboundRequestMethods.set(message.id, message.method);
        }
        const entry = {
          id: crypto.randomUUID(),
          timestamp: /* @__PURE__ */ new Date(),
          direction: "request",
          origin,
          message
        };
        this.dispatchTypedEvent("message", entry);
      },
      trackResponse: (message, origin) => {
        const responseId = message.id;
        if (origin === "server" && responseId !== void 0) {
          const method = this.outboundRequestMethods.get(responseId);
          this.outboundRequestMethods.delete(responseId);
          if (method !== void 0) {
            this.lastAnsweredRequestByMethod.set(method, responseId);
          }
        }
        const entry = {
          id: crypto.randomUUID(),
          timestamp: /* @__PURE__ */ new Date(),
          direction: "response",
          origin,
          message
        };
        this.dispatchTypedEvent("message", entry);
      },
      trackNotification: (message, origin) => {
        const entry = {
          id: crypto.randomUUID(),
          timestamp: /* @__PURE__ */ new Date(),
          direction: "notification",
          origin,
          message
        };
        this.dispatchTypedEvent("message", entry);
      }
    };
  }
  attachTransportListeners(baseTransport) {
    baseTransport.onclose = () => {
      if (this.disconnecting) return;
      if (this.status === "disconnected") return;
      if (this.status !== "error") {
        this.status = "disconnected";
        this.dispatchTypedEvent("statusChange", this.status);
      }
      this.clearAndAnnouncePendingPeerRequests();
      this.rejectPendingRawWireRequests("Connection closed");
      this.dispatchTypedEvent("disconnect");
    };
    baseTransport.onerror = (error) => {
      if (this.status === "connecting") return;
      this.status = "error";
      this.dispatchTypedEvent("statusChange", this.status);
      this.dispatchTypedEvent("error", error);
    };
  }
  /**
   * Build RequestOptions for SDK client calls (timeout, resetTimeoutOnProgress, onprogress).
   * When timeout is unset, SDK uses DEFAULT_REQUEST_TIMEOUT_MSEC (60s).
   *
   * When progress is enabled, we pass a per-request onprogress so the SDK routes progress and
   * runs timeout reset. The SDK injects progressToken: messageId; we do not expose the caller's
   * token to the server. We collect it from metadata and inject it into dispatched progressNotification
   * events only, so listeners can correlate progress with the request that triggered it.
   *
   * @param progressToken Optional token from request metadata; injected into progressNotification
   * events when provided (not sent to server).
   */
  /**
   * Merge per-call metadata with this client's `defaultMetadata` (from
   * `InspectorClientOptions.defaultMetadata`, set from
   * `InspectorServerSettings.metadata`). Call-time keys override defaults.
   * Returns `undefined` when the combined map is empty so callers can skip
   * injecting an empty `_meta` field.
   */
  mergeMeta(callMetadata) {
    const defaults = this.defaultMetadata;
    const logMeta = this.protocolEra === "modern" && this.modernLogLevel ? { [LOG_LEVEL_META_KEY]: this.modernLogLevel } : void 0;
    const merged = {
      ...defaults ?? {},
      ...logMeta ?? {},
      ...callMetadata ?? {}
    };
    return Object.keys(merged).length > 0 ? merged : void 0;
  }
  getRequestOptions(progressToken, signal) {
    const opts = {
      resetTimeoutOnProgress: this.resetTimeoutOnProgress
    };
    if (this.requestTimeout !== void 0) {
      opts.timeout = this.requestTimeout;
    }
    if (signal) {
      opts.signal = signal;
    }
    if (this.progress) {
      const token = progressToken;
      const onprogress = (progress) => {
        const payload = {
          ...progress,
          ...token != null && { progressToken: token }
        };
        this.dispatchTypedEvent("progressNotification", payload);
      };
      opts.onprogress = onprogress;
    }
    return opts;
  }
  /**
   * {@link getRequestOptions} plus the per-call `cacheMode` for the SDK's
   * cacheable verbs (the high-level `client.listTools()` / `listPrompts()` /
   * `listResources()` / `listResourceTemplates()` used by the `listAll*`
   * aggregate methods below). `cacheMode` is only honored by those wrappers —
   * the single-page `client.request` path ignores it — so it lives here rather
   * than in `getRequestOptions`. Omitted when unset so the SDK default
   * (`'use'`) applies.
   */
  getCacheableRequestOptions(cacheMode) {
    const opts = this.getRequestOptions();
    if (cacheMode !== void 0) {
      opts.cacheMode = cacheMode;
    }
    return opts;
  }
  /**
   * Build the `params` for the aggregate `listAll*` verbs: merge call metadata
   * with `defaultMetadata` and wrap as `{ _meta }`, or `undefined` when empty
   * (so the SDK skips an empty `_meta`). Shared by all four `listAll*` methods
   * so the merge/omit branch is defined once.
   */
  aggregateListParams(metadata) {
    const effectiveMeta = this.mergeMeta(metadata);
    return effectiveMeta ? { _meta: effectiveMeta } : void 0;
  }
  isHttpOAuthConfig() {
    const serverType = getServerType(this.transportConfig);
    return (serverType === "sse" || serverType === "streamable-http") && !!this.oauthManager;
  }
  /**
   * True when task status is completed, failed, or cancelled.
   * We use this private helper instead of the SDK's experimental isTerminal()
   * to avoid depending on experimental API and to get a type predicate so
   * TypeScript narrows status to "completed" | "failed" | "cancelled" after the check.
   */
  static isTerminalTaskStatus(status) {
    return status === "completed" || status === "failed" || status === "cancelled";
  }
  /**
   * Route a receiver (server-initiated) task-augmented `sampling/createMessage`
   * or `elicitation/create` response around the v2 Client's result validation.
   *
   * SDK v2's `Client` wraps every spec request handler (`_wrapHandler`) to
   * validate the result it returns — for sampling/elicitation it checks the
   * value against `CreateMessageResult` / `ElicitResult` and rejects anything
   * else with a `-32602`. The 2025-11-25 task flow answers a task-augmented
   * request with a `CreateTaskResult` (`{ task }`), which that validation
   * rejects — breaking server-initiated tasks that worked on the legacy client.
   *
   * There is no public seam to opt a handler out of result validation, so we
   * swap the wrapped entry in the Protocol's private `_requestHandlers` map for
   * one that dispatches the task-augmented branch straight through the raw
   * handler (whose `{ task }` return then rides the legacy codec's pass-through
   * `encodeResult` to the wire), while ordinary (non-task) requests keep the
   * validating path. Mirrors the bypass a legacy server needs to emit `{ task }`.
   * Delete once the SDK models task-augmented results natively (see #1624 stack).
   */
  installReceiverTaskResponseBypass(method, rawHandler) {
    if (!this.client) return;
    const internal = this.client;
    const validating = internal._requestHandlers.get(method);
    if (!validating) return;
    internal._requestHandlers.set(method, (request, ctx) => {
      const task = request?.params?.task;
      if (this.tasksCapabilityAdvertised && task != null) {
        return rawHandler(request);
      }
      return validating(request, ctx);
    });
  }
  createReceiverTask(opts) {
    const taskId = crypto.randomUUID();
    const ttlMs = opts.ttl ?? (typeof this.receiverTaskTtlMs === "function" ? this.receiverTaskTtlMs() : this.receiverTaskTtlMs);
    const now = (/* @__PURE__ */ new Date()).toISOString();
    const task = {
      taskId,
      status: opts.initialStatus,
      ttl: ttlMs,
      createdAt: now,
      lastUpdatedAt: now,
      ...opts.pollInterval != null && { pollInterval: opts.pollInterval },
      ...opts.statusMessage != null && { statusMessage: opts.statusMessage }
    };
    let resolvePayload;
    let rejectPayload;
    const payloadPromise = new Promise((resolve4, reject) => {
      resolvePayload = resolve4;
      rejectPayload = reject;
    });
    void payloadPromise.catch(() => {
    });
    const record = {
      task,
      payloadPromise,
      resolvePayload,
      rejectPayload
    };
    record.cleanupTimeoutId = setTimeout(() => {
      record.cleanupTimeoutId = void 0;
      this.receiverTaskRecords.delete(taskId);
    }, ttlMs);
    this.receiverTaskRecords.set(taskId, record);
    return record;
  }
  emitReceiverTaskStatus(task) {
    if (!this.client) return;
    try {
      const notification = TaskStatusNotificationSchema.parse({
        method: "notifications/tasks/status",
        params: task
      });
      this.client.notification(notification).catch((err) => {
        this.logger.warn(
          { err, taskId: task.taskId },
          "receiver task status notification failed"
        );
      });
    } catch (err) {
      this.logger.warn(
        { err, taskId: task.taskId },
        "receiver task status notification failed"
      );
    }
  }
  upsertReceiverTask(updatedTask) {
    const record = this.receiverTaskRecords.get(updatedTask.taskId);
    if (record) {
      record.task = updatedTask;
      this.emitReceiverTaskStatus(updatedTask);
    }
  }
  getReceiverTask(taskId) {
    return this.receiverTaskRecords.get(taskId);
  }
  listReceiverTasks() {
    return Array.from(this.receiverTaskRecords.values()).map((r) => r.task);
  }
  async getReceiverTaskPayload(taskId) {
    const record = this.receiverTaskRecords.get(taskId);
    if (!record) {
      throw new ProtocolError2(
        ProtocolErrorCode2.InvalidParams,
        `Unknown taskId: ${taskId}`
      );
    }
    return record.payloadPromise;
  }
  cancelReceiverTask(taskId) {
    const record = this.receiverTaskRecords.get(taskId);
    if (!record) {
      throw new ProtocolError2(
        ProtocolErrorCode2.InvalidParams,
        `Unknown taskId: ${taskId}`
      );
    }
    if (_InspectorClient.isTerminalTaskStatus(record.task.status)) {
      return record.task;
    }
    const now = (/* @__PURE__ */ new Date()).toISOString();
    const updatedTask = {
      ...record.task,
      status: "cancelled",
      lastUpdatedAt: now
    };
    record.task = updatedTask;
    record.rejectPayload(new Error("Task cancelled"));
    if (record.cleanupTimeoutId != null) {
      clearTimeout(record.cleanupTimeoutId);
      record.cleanupTimeoutId = void 0;
    }
    this.emitReceiverTaskStatus(updatedTask);
    return updatedTask;
  }
  /**
   * Drop the cached MCP transport without a full disconnect() teardown.
   * Used when a pre-auth connect failed or tokens arrived after an unauthenticated
   * transport was created, so the next connect() can attach authProvider.
   */
  async dropCachedTransport() {
    if (!this.baseTransport && !this.transport) {
      this.transportHasAuthProvider = false;
      return;
    }
    try {
      await this.client?.close();
    } catch {
    }
    this.baseTransport = null;
    this.transport = null;
    this.transportHasAuthProvider = false;
  }
  /**
   * Register the handlers for requests the *server* makes of *us* —
   * `roots/list`, `sampling/createMessage`, `elicitation/create`, and the
   * receiver-side `tasks/*` polls.
   *
   * MUST be called before `client.connect()`. The matching capabilities are
   * advertised on the `Client` at construction time, so from the moment
   * `connect()` sends `notifications/initialized` the server is entitled to
   * issue any of these requests. Registering afterwards leaves a window in
   * which the SDK `Client` has no handler and answers `-32601 Method not
   * found` — which is exactly what a server that asks for roots the instant it
   * is initialized (e.g. `server-filesystem`, which learns its allowed
   * directories that way) hits, while a server that asks later does not (#1797).
   *
   * Nothing here depends on the server's capabilities — only on constructor-set
   * state — so there is nothing to wait for. Its sibling
   * {@link registerPeerNotificationHandlers} does the same for the one
   * notification handler in that position; the notification handlers that *do*
   * gate on `this.capabilities` stay in `connect()`, after the handshake.
   */
  registerPeerRequestHandlers() {
    if (this.samplingCapabilityAdvertised && this.client) {
      const samplingHandler = (request) => {
        const paramsTask = request.params?.task;
        if (this.tasksCapabilityAdvertised && paramsTask != null) {
          const record = this.createReceiverTask({
            ttl: paramsTask.ttl,
            initialStatus: "input_required",
            statusMessage: "Awaiting user input"
          });
          void (async () => {
            const samplingRequest = new SamplingCreateMessage(
              request,
              (result) => {
                record.resolvePayload(result);
                const now = (/* @__PURE__ */ new Date()).toISOString();
                const updated = {
                  ...record.task,
                  status: "completed",
                  lastUpdatedAt: now
                };
                record.task = updated;
                this.upsertReceiverTask(updated);
              },
              (error) => {
                record.rejectPayload(error);
                const now = (/* @__PURE__ */ new Date()).toISOString();
                const updated = {
                  ...record.task,
                  status: "failed",
                  lastUpdatedAt: now,
                  statusMessage: error instanceof Error ? error.message : String(error)
                };
                record.task = updated;
                this.upsertReceiverTask(updated);
              },
              (id) => this.removePendingSample(id)
            );
            this.addPendingSample(samplingRequest);
          })();
          const taskResult = { task: record.task };
          return Promise.resolve(taskResult);
        }
        return this.enqueuePendingSample(request, "server-request");
      };
      this.client.setRequestHandler("sampling/createMessage", samplingHandler);
      if (this.tasksCapabilityAdvertised) {
        this.installReceiverTaskResponseBypass(
          "sampling/createMessage",
          samplingHandler
        );
      }
    }
    if (this.elicitationCapabilityAdvertised && this.client) {
      const elicitHandler = (request) => {
        const paramsTask = request.params?.task;
        if (this.tasksCapabilityAdvertised && paramsTask != null) {
          const record = this.createReceiverTask({
            ttl: paramsTask.ttl,
            initialStatus: "input_required",
            statusMessage: "Awaiting user input"
          });
          void (async () => {
            const elicitationRequest = new ElicitationCreateMessage(
              request,
              (result) => {
                record.resolvePayload(result);
                const now = (/* @__PURE__ */ new Date()).toISOString();
                const updated = {
                  ...record.task,
                  status: "completed",
                  lastUpdatedAt: now
                };
                record.task = updated;
                this.upsertReceiverTask(updated);
              },
              (id) => this.removePendingElicitation(id),
              (error) => {
                record.rejectPayload(error);
                const now = (/* @__PURE__ */ new Date()).toISOString();
                const updated = {
                  ...record.task,
                  status: "failed",
                  lastUpdatedAt: now,
                  statusMessage: error.message
                };
                record.task = updated;
                this.upsertReceiverTask(updated);
              }
            );
            this.addPendingElicitation(elicitationRequest);
          })();
          const taskResult = { task: record.task };
          return Promise.resolve(taskResult);
        }
        return this.enqueuePendingElicitation(request, "server-request");
      };
      this.client.setRequestHandler("elicitation/create", elicitHandler);
      if (this.tasksCapabilityAdvertised) {
        this.installReceiverTaskResponseBypass(
          "elicitation/create",
          elicitHandler
        );
      }
    }
    if (this.rootsCapabilityAdvertised && this.client) {
      this.client.setRequestHandler("roots/list", async () => {
        return { roots: this.roots ?? [] };
      });
    }
    if (this.tasksCapabilityAdvertised && this.client) {
      this.client.setRequestHandler(
        "tasks/list",
        { params: ListTasksRequestSchema.shape.params },
        async () => ({ tasks: this.listReceiverTasks() })
      );
      this.client.setRequestHandler(
        "tasks/get",
        { params: GetTaskRequestSchema.shape.params },
        async (params) => {
          const record = this.getReceiverTask(params.taskId);
          if (!record) {
            throw new ProtocolError2(
              ProtocolErrorCode2.InvalidParams,
              `Unknown taskId: ${params.taskId}`
            );
          }
          return record.task;
        }
      );
      this.client.setRequestHandler(
        "tasks/result",
        { params: GetTaskPayloadRequestSchema.shape.params },
        async (params) => this.getReceiverTaskPayload(params.taskId)
      );
      this.client.setRequestHandler(
        "tasks/cancel",
        { params: CancelTaskRequestSchema.shape.params },
        async (params) => this.cancelReceiverTask(params.taskId)
      );
    }
  }
  /**
   * Register the inbound *notification* handlers that depend on nothing but
   * constructor-set state, and so belong before the handshake for the same
   * reason as {@link registerPeerRequestHandlers} (#1797).
   *
   * `notifications/roots/list_changed` is the only one — and note it is a
   * **client**→server notification in the spec (`ClientNotification`): we send
   * it from {@link setRoots}, servers do not normally send it to us. This
   * inbound handler is defensive coverage for a non-conformant or experimental
   * server, and its body dispatches `rootsChange` with our own already-known
   * roots, i.e. a refresh signal carrying no new data. It sits here for
   * consistency with the request handlers — it gates on no server capability,
   * so there is nothing to wait for, and an unhandled notification is dropped
   * silently by the SDK (no wire error) rather than answered `-32601`.
   *
   * The remaining listChanged handlers gate on `this.capabilities`, which is
   * not populated until `fetchServerInfo()` runs, so they stay in `connect()`.
   */
  registerPeerNotificationHandlers() {
    if (!this.client) return;
    this.client.setNotificationHandler(
      "notifications/roots/list_changed",
      async () => {
        this.dispatchTypedEvent("rootsChange", [...this.roots ?? []]);
      }
    );
  }
  /**
   * Stop the receiver tasks' TTL timers and drop the records.
   *
   * These are tasks a *server* created with us, so they belong to the session
   * that created them: `listReceiverTasks()` is what the `tasks/list` handler
   * answers with, and a record surviving into the next session would report a
   * task the new server never created. `disconnect()` clears them, and so does
   * `connect()` — the auth-recovery retry reconnects the *same* client
   * instance, so ending the session isn't the only way a new one begins
   * (#1797).
   */
  clearReceiverTasks() {
    for (const record of this.receiverTaskRecords.values()) {
      if (record.cleanupTimeoutId != null) {
        clearTimeout(record.cleanupTimeoutId);
      }
    }
    this.receiverTaskRecords.clear();
  }
  /**
   * Reset the modern listen-stream cluster: the subscribed set, the stream
   * state derived from it, and the reconnect machinery that reports on it.
   *
   * One helper because these move together — the rest of the file derives the
   * stream's `active` from `subscribedResources.size > 0`, so clearing one
   * without the other leaves a combination those readers treat as impossible,
   * and a surviving `modernReconnectAttempts` makes a *new* session's first
   * drop back off as if it were the old session's nth. Both axes are announced:
   * every other mutation of the set dispatches, and so does the stream state.
   *
   * Closes the stream itself, best-effort. `disconnect()` is the obvious caller
   * with a live one, but not the only one: an `onerror` without an `onclose`
   * leaves the transport up, and `connect()` then reuses it — so the reference
   * dropped here can be the last one to a stream still open on the server.
   */
  resetSubscriptionStream() {
    const closing = this.modernSubscription;
    this.subscribedResources.clear();
    this.modernListenGeneration++;
    this.clearModernReconnectTimer();
    this.modernReconnectAttempts = 0;
    this.modernSubscription = null;
    this.setModernStreamState(INACTIVE_SUBSCRIPTION_STREAM_STATE);
    this.dispatchSubscriptionsChange();
    if (closing) void closeSubscriptionBestEffort(closing);
  }
  /**
   * Drop the session-scoped state a *new* session would misread — anything the
   * next server could be told about, or that would change how we treat its
   * traffic. State that only needs settling on the way out (the peer-request
   * queues, the raw-wire map, the in-flight tool call) is not reset here — the
   * in-flight tool call because `callTool`'s own `finally` releases it once the
   * SDK rejects it, the other two because they are settled *end*-clean, by
   * `disconnect()` and the crash path, so that a consumer handling the
   * `disconnect` event already sees them empty. That is a difference in when,
   * not in whether: the routes out do not cover every route *in* (an `onerror`
   * without an `onclose` runs neither), so `connect()` sweeps those two beside
   * this call as a backstop — see the comment there.
   *
   * `disconnect()` touches all of this on the way out too — two members through
   * the same helpers, three hand-rolled in both places — so a sixth member
   * added here has to be added there as well. One of the three is *paired*
   * rather than duplicated: `modernLogLevel` is re-derived here and blanked
   * there, deliberately (see the comment at that site). It is not the only
   * way a session ends — a crash, or a failed connect the caller retries on
   * this same instance (the auth-recovery path), both leave it behind. Called
   * start-clean from `connect()` so every route in is covered.
   *
   * Each member has a symptom, not just untidiness: a stale `subscribedResources`
   * entry makes the modern `subscribeToResource` early-return, so the user's
   * Subscribe click silently sends nothing to the new server; a stale
   * `cancelledTaskIds` entry mislabels a *new* task sharing the id as
   * `cancelled` rather than `failed`; a stale subscription stream state reads
   * `active` for a set that is now empty, which every reader of it treats as
   * impossible; a receiver-task record is reported to the new server by
   * `tasks/list`; and an un-aborted `taskInputAbortControllers`
   * entry delays a paused poll loop unwinding — both registration sites release
   * in a `finally`, so nothing leaks permanently; the abort just closes the
   * window between the crash and the unwind (#1797).
   */
  resetSessionState() {
    this.clearReceiverTasks();
    this.resetSubscriptionStream();
    this.cancelledTaskIds.clear();
    this.outboundRequestMethods.clear();
    this.lastAnsweredRequestByMethod.clear();
    for (const [, controller] of this.taskInputAbortControllers) {
      controller.abort(new Error("Connection ended"));
    }
    this.taskInputAbortControllers.clear();
    this.modernLogLevel = resolveModernLogLevel(this.serverSettings);
  }
  /**
   * Settle and drop the queued peer requests (sampling / elicitation).
   *
   * Every entry is settled before being dropped rather than discarded: an
   * elicitation so an error-path `awaitUrlElicitation` — which blocks
   * `callTool` — doesn't hang forever, and a sample so the *server* gets a
   * response frame for the request we accepted (the transport can outlive a
   * failed attempt; see the `connect()` catch). Callers dispatch the change
   * events themselves:
   * `disconnect()` batches them with its other teardown dispatches, and
   * {@link clearAndAnnouncePendingPeerRequests} emits them immediately
   * everywhere else.
   */
  clearPendingPeerRequests() {
    for (const sample of this.pendingSamples) {
      sample.cancel();
    }
    this.pendingSamples = [];
    for (const elicitation of this.pendingElicitations) {
      elicitation.cancel();
    }
    this.pendingElicitations = [];
  }
  /**
   * {@link clearPendingPeerRequests} plus the change events, for every route
   * that drops a queue without going through `disconnect()`: the routes *out*
   * that end a connection some other way, plus the top of `connect()` as a
   * backstop for the one route in that settles nothing (an `onerror` without an
   * `onclose` — see the comment at that call). Named as a category rather than
   * counted, because the set has grown before. (One of the routes out doesn't
   * always end the connection: when a `connect()` failure leaves an auth
   * provider holding the transport open, the caller re-authenticates and
   * retries over it, and what's dropped is the queue left by the attempt that
   * failed.)
   *
   * The events are the load-bearing half: `usePendingClientRequests` tracks its
   * own state off them, so clearing the arrays without dispatching leaves the
   * web pending-request modal on screen for a connection that is gone. Guarded
   * on a non-empty queue so the paths that overlap (a `connect()` failure whose
   * `dropCachedTransport` also fires `onclose`) announce it once.
   */
  clearAndAnnouncePendingPeerRequests() {
    if (this.pendingSamples.length === 0 && this.pendingElicitations.length === 0) {
      return;
    }
    this.clearPendingPeerRequests();
    this.dispatchTypedEvent("pendingSamplesChange", this.pendingSamples);
    this.dispatchTypedEvent(
      "pendingElicitationsChange",
      this.pendingElicitations
    );
  }
  /**
   * Connect to the MCP server
   */
  async connect() {
    if (!this.client) {
      throw new Error("Client not initialized");
    }
    if (this.status === "connected") {
      return;
    }
    this.resetSessionState();
    this.clearAndAnnouncePendingPeerRequests();
    this.rejectPendingRawWireRequests("Connection ended");
    const oauthManager = this.oauthManager;
    if (this.baseTransport && this.isHttpOAuthConfig() && oauthManager && !this.transportHasAuthProvider && !oauthManager.isEnterpriseManaged() && await oauthManager.isOAuthAuthorized()) {
      await this.dropCachedTransport();
    }
    if (!this.baseTransport) {
      const transportOptions = {
        fetchFn: this.fetchFn,
        pipeStderr: this.pipeStderr,
        onStderr: (entry) => {
          this.dispatchStderrLog(entry);
        },
        onFetchRequest: (entry) => {
          this.dispatchFetchRequest({ ...entry, category: "transport" });
        },
        onFetchResponseBody: (id, body) => {
          this.dispatchFetchRequestBodyUpdate(id, body);
        },
        ...this.serverSettings && { settings: this.serverSettings }
      };
      if (this.isHttpOAuthConfig() && oauthManager) {
        if (oauthManager.isEnterpriseManaged()) {
          await oauthManager.trySilentEnterpriseManagedAuth();
          const provider = await oauthManager.createOAuthProviderForTransport();
          const tokens = await provider.tokens();
          if (!tokens?.access_token) {
            const err = new Error(
              "Unauthorized: EMA resource access token unavailable"
            );
            err.status = 401;
            err.code = 401;
            throw err;
          }
          transportOptions.authProvider = provider;
        } else if (await oauthManager.isOAuthAuthorized()) {
          transportOptions.authProvider = await oauthManager.createOAuthProviderForTransport();
        }
      }
      if (this.directAuthRecovery && this.directAuthRecoveryActive !== false && this.isHttpOAuthConfig() && oauthManager && // No stored tokens means no authProvider (see above), and then a 401 on
      // the era-negotiation probe reaches the SDK as a raw `SdkHttpError`.
      // The probe's classifier ignores the HTTP status — it only looks for a
      // JSON-RPC error body — so it verdicts "not a modern server", and pin
      // ("modern") mode rethrows that as ERA_NEGOTIATION_FAILED with the 401
      // discarded entirely: no status, not even a cause. Intercepting makes
      // the 401 a typed AuthChallengeError, which survives the probe as
      // `data.cause` for `findNestedAuthError` to recover (#1805).
      //
      // WORKAROUND (#1807, upstream modelcontextprotocol/typescript-sdk#2561):
      // remove this clause once the SDK classifies a probe 401/403 as
      // auth-required. `findNestedAuthError` is the permanent fix; the
      // `|| this.probesProtocolEra()` clause below exists only to compensate
      // for that upstream gap and should be deleted with it.
      //
      // Known, accepted side effect of turning intercept on with no stored
      // tokens: `parseAuthChallengeFromResponse` treats 403 as a challenge
      // too, so a probe answered 403 for a *non-auth* reason (a gateway
      // rejecting the unknown `server/discover` method, say) now starts OAuth
      // discovery instead of letting "auto" fall back to the legacy
      // `initialize`. The outcome is a surfaced `oauthError`, not a hang, and
      // it goes away with this clause.
      (transportOptions.authProvider || this.probesProtocolEra())) {
        transportOptions.interceptAuthChallenges = true;
      }
      this.transportHasAuthProvider = !!transportOptions.authProvider;
      const { transport: baseTransport } = this.transportClientFactory(
        this.transportConfig,
        transportOptions
      );
      this.baseTransport = baseTransport;
      if (this.directAuthRecovery) {
        this.directAuthRecoveryActive = !(baseTransport instanceof RemoteClientTransport);
      }
      if (baseTransport instanceof RemoteClientTransport && oauthManager && this.isHttpOAuthConfig()) {
        baseTransport.setAuthRecovery({
          handleAuthChallenge: (challenge, options) => oauthManager.handleAuthChallenge(challenge, options),
          pushAuthState: () => this.pushRemoteAuthState()
        });
        baseTransport.setOnAuthChallenge((challenge) => {
          void this.handleAmbientAuthChallenge(challenge);
        });
      }
      const messageTracking = this.createMessageTrackingCallbacks();
      this.transport = new MessageTrackingTransport(
        baseTransport,
        messageTracking,
        {
          rewriteIncomingResult: (message) => this.rewriteModernTaskResult(message),
          consumeIncomingResponse: (message) => this.consumeRawWireResponse(message)
        }
      );
      this.attachTransportListeners(this.baseTransport);
    }
    if (!this.transport) {
      throw new Error("Transport not initialized");
    }
    try {
      this.status = "connecting";
      this.dispatchTypedEvent("statusChange", this.status);
      this.registerPeerRequestHandlers();
      this.registerPeerNotificationHandlers();
      const connectTimeoutMs = this.serverSettings?.connectionTimeout ?? 0;
      const connectPromise = this.client.connect(this.transport).catch((err) => {
        throw findNestedAuthError(err) ?? err;
      });
      let recoveredByNestedConnect = false;
      const runConnect = async (attempt) => {
        if (attempt > 0) {
          recoveredByNestedConnect = true;
          if (this.status !== "connected") {
            throw new Error(
              "Connection closed during authorization recovery, after re-authorizing successfully"
            );
          }
          return;
        }
        if (connectTimeoutMs > 0) {
          connectPromise.catch(() => {
          });
          let timer;
          const timeoutPromise = new Promise((_, reject) => {
            timer = setTimeout(
              () => reject(
                new Error(
                  `Connection timed out after ${connectTimeoutMs} ms`
                )
              ),
              connectTimeoutMs
            );
          });
          try {
            await Promise.race([connectPromise, timeoutPromise]);
          } finally {
            if (timer) clearTimeout(timer);
          }
        } else {
          await connectPromise;
        }
      };
      try {
        await this.invokeMcpClient(runConnect);
      } catch (err) {
        if (connectTimeoutMs > 0) {
          await this.disconnect().catch(() => {
          });
        }
        throw err;
      }
      if (recoveredByNestedConnect) {
        return;
      }
      this.status = "connected";
      this.dispatchTypedEvent("statusChange", this.status);
      await this.fetchServerInfo();
      if (this.initialLoggingLevel && this.capabilities?.logging) {
        await this.client.setLoggingLevel(
          this.initialLoggingLevel,
          this.getRequestOptions()
        );
      }
      if (this.client) {
        if (this.listChangedNotifications.tools && this.capabilities?.tools?.listChanged) {
          this.client.setNotificationHandler(
            "notifications/tools/list_changed",
            async () => {
              this.dispatchTypedEvent("toolsListChanged");
            }
          );
        }
        if (this.listChangedNotifications.resources && this.capabilities?.resources?.listChanged) {
          this.client.setNotificationHandler(
            "notifications/resources/list_changed",
            async () => {
              this.dispatchTypedEvent("resourcesListChanged");
              this.dispatchTypedEvent("resourceTemplatesListChanged");
            }
          );
        }
        if (this.listChangedNotifications.prompts && this.capabilities?.prompts?.listChanged) {
          this.client.setNotificationHandler(
            "notifications/prompts/list_changed",
            async () => {
              this.dispatchTypedEvent("promptsListChanged");
            }
          );
        }
        if (this.capabilities?.tasks) {
          this.client.setNotificationHandler(
            "notifications/tasks/list_changed",
            { params: TasksListChangedNotificationSchema.shape.params },
            async () => {
              this.dispatchTypedEvent("tasksListChanged");
            }
          );
          this.client.setNotificationHandler(
            "notifications/tasks/status",
            { params: TaskStatusNotificationSchema.shape.params },
            async (params) => {
              const task = params;
              this.dispatchTypedEvent("taskStatusChange", {
                taskId: task.taskId,
                task
              });
            }
          );
        }
        if (this.capabilities?.resources?.subscribe === true) {
          this.client.setNotificationHandler(
            "notifications/resources/updated",
            async (notification) => {
              const uri = notification.params.uri;
              if (this.subscribedResources.has(uri)) {
                this.dispatchTypedEvent("resourceUpdated", { uri });
              }
            }
          );
        }
        if (this.urlElicitationCapabilityAdvertised) {
          this.client.setNotificationHandler(
            "notifications/elicitation/complete",
            async (notification) => {
              const { elicitationId } = notification.params;
              const pending = this.pendingElicitations.find(
                (e) => e.request.params?.mode === "url" && e.request.params?.elicitationId === elicitationId
              );
              if (pending) {
                pending.completeIfPending();
              }
            }
          );
        }
      }
      await this.openModernListenStreamOnConnect();
      if (this.status === "connected" && !this.disconnecting) {
        this.dispatchTypedEvent("connect");
      }
    } catch (error) {
      if (!isConnectAuthRecoveryError(error)) {
        this.status = "error";
        this.dispatchTypedEvent("statusChange", this.status);
      }
      if (this.baseTransport && !this.transportHasAuthProvider) {
        await this.dropCachedTransport();
      }
      this.clearAndAnnouncePendingPeerRequests();
      throw error;
    }
  }
  /**
   * Disconnect from the MCP server.
   * @param safeDisconnectTimeout If > 0, poll every 10ms until SDK _responseHandlers is empty or this many ms have elapsed, then close. Default 0 = close immediately.
   */
  async disconnect(safeDisconnectTimeout = 0) {
    this.disconnecting = true;
    try {
      if (this.client) {
        if (safeDisconnectTimeout > 0) {
          const protocol = this.client;
          const handlers = protocol._responseHandlers;
          const deadline = Date.now() + safeDisconnectTimeout;
          while (handlers?.size !== void 0 && handlers.size > 0 && Date.now() < deadline) {
            await new Promise((r) => setTimeout(r, 10));
          }
        }
        try {
          await this.client.close();
        } catch {
        }
      }
    } finally {
      this.disconnecting = false;
    }
    this.baseTransport = null;
    this.transport = null;
    this.transportHasAuthProvider = false;
    this.clearPendingPeerRequests();
    if (this.status !== "disconnected") {
      this.status = "disconnected";
      this.dispatchTypedEvent("statusChange", this.status);
      this.dispatchTypedEvent("disconnect");
    }
    this.resetSubscriptionStream();
    this.cancelledTaskIds.clear();
    this.rejectPendingRawWireRequests("Disconnected");
    for (const [, controller] of this.taskInputAbortControllers) {
      controller.abort(new Error("Disconnected"));
    }
    this.taskInputAbortControllers.clear();
    this.activeToolCallAbortController?.abort("Disconnected");
    this.activeToolCallAbortController = void 0;
    this.clearReceiverTasks();
    this.appRendererClientProxy = null;
    this.capabilities = void 0;
    this.serverInfo = void 0;
    this.instructions = void 0;
    this.protocolVersion = void 0;
    this.protocolEra = void 0;
    this.discoverResult = void 0;
    this.excludedTools = [];
    this.modernLogLevel = void 0;
    this.dispatchTypedEvent("pendingSamplesChange", this.pendingSamples);
    this.dispatchTypedEvent(
      "pendingElicitationsChange",
      this.pendingElicitations
    );
    this.dispatchTypedEvent("capabilitiesChange", this.capabilities);
    this.dispatchTypedEvent("serverInfoChange", this.serverInfo);
    this.dispatchTypedEvent("instructionsChange", this.instructions);
    this.dispatchTypedEvent("protocolVersionChange", this.protocolVersion);
    this.dispatchTypedEvent("protocolEraChange", this.protocolEra);
    this.dispatchTypedEvent("discoverResultChange", this.discoverResult);
    this.dispatchTypedEvent("excludedToolsChange", this.excludedTools);
  }
  /**
   * Returns a client proxy for use by AppRenderer / @mcp-ui. Delegates to the
   * internal MCP Client. Returns null when not connected. Use this instead of
   * accessing the raw client so behavior can be adapted here later if needed.
   */
  getAppRendererClient() {
    if (!this.client || this.status !== "connected") return null;
    if (this.appRendererClientProxy !== null)
      return this.appRendererClientProxy;
    const target = this.client;
    this.appRendererClientProxy = new Proxy(this.client, {
      get(proxyTarget, prop, receiver) {
        const value = Reflect.get(proxyTarget, prop, receiver);
        if (prop === "setNotificationHandler" && typeof value === "function") {
          return (schemaOrMethod, ...rest) => {
            const method = typeof schemaOrMethod === "string" ? schemaOrMethod : notificationMethodFromSchema(schemaOrMethod) ?? schemaOrMethod;
            return value.apply(target, [
              method,
              ...rest
            ]);
          };
        }
        return value;
      }
    });
    return this.appRendererClientProxy;
  }
  /**
   * Send a ping request to the server. Resolves when the server responds.
   */
  async ping() {
    if (!this.client) {
      throw new Error("Client not initialized");
    }
    await this.client.request(
      { method: "ping" },
      EmptyResultSchema,
      this.getRequestOptions()
    );
  }
  /**
   * Get the current connection status
   */
  getStatus() {
    return this.status;
  }
  /**
   * Get the MCP server configuration used to create this client
   */
  getTransportConfig() {
    return this.transportConfig;
  }
  /**
   * Get the server type (stdio, sse, or streamable-http)
   */
  getServerType() {
    return getServerType(this.transportConfig);
  }
  /**
   * Get task capabilities from server
   * @returns Task capabilities or undefined if not supported
   */
  getTaskCapabilities() {
    if (!this.capabilities?.tasks) {
      return void 0;
    }
    return {
      list: !!this.capabilities.tasks.list,
      cancel: !!this.capabilities.tasks.cancel
    };
  }
  /**
   * True when the connection is modern (2026-07-28) AND the server advertised
   * the `io.modelcontextprotocol/tasks` extension (SEP-2663) in its
   * `server/discover` capabilities. Modern task methods (`tasks/get`,
   * `tasks/update`, `tasks/cancel`) and the "unsolicited task handle" behavior
   * are gated on this — legacy servers use `capabilities.tasks` and the
   * `tasks/list`-backed flow instead. Exposed so the Tasks tab and the modern
   * task store gate on the extension rather than the legacy capability.
   */
  isTasksExtensionNegotiated() {
    return this.isModernEra() && this.capabilities?.extensions?.[TASKS_EXTENSION_KEY] !== void 0;
  }
  /**
   * Build the full modern (2026-07-28) per-request envelope for a RAW tasks/*
   * request. The SDK's codec normally stamps this envelope, but raw requests
   * bypass the codec, and the modern server rejects a request whose
   * `MCP-Protocol-Version` header names 2026-07-28 but omits the required
   * envelope `_meta` keys (`protocolVersion`, `clientInfo`, plus
   * `clientCapabilities` carrying the tasks extension). We reproduce it here.
   */
  withModernTaskEnvelope(params) {
    const existingMeta = params._meta ?? {};
    const clientCapabilities = {
      ...this.clientCapabilities,
      // Force-stamp the tasks extension regardless of what the client
      // advertised at construction: the raw `tasks/*` channel requires it, and
      // a user may disable general tasks advertisement via `advertisedExtensions`
      // (#1738). So this stamp is load-bearing, not a redundant re-add.
      extensions: {
        ...this.clientCapabilities.extensions,
        [TASKS_EXTENSION_KEY]: {}
      }
    };
    const protocolVersion = this.getProtocolVersion() ?? MODERN_PROTOCOL_VERSION2;
    return {
      ...params,
      _meta: {
        ...existingMeta,
        [PROTOCOL_VERSION_META_KEY]: protocolVersion,
        [CLIENT_INFO_META_KEY]: this.clientInfo,
        [CLIENT_CAPABILITIES_META_KEY]: clientCapabilities
      }
    };
  }
  /**
   * Transport-level rewrite of a modern (SEP-2663) `CreateTaskResult`
   * (`resultType: "task"`) — the one task frame the SDK v2 codec rejects (tasks
   * were removed, so the codec knows only `complete`/`input_required`). The true
   * frame is already logged by `trackResponse`; here we hand the SDK a benign
   * `CallToolResult` that carries the real `DetailedTask` under
   * {@link MODERN_TASK_HANDLE_META}, where {@link pollTaskToolCall} reads it to
   * drive the poll. Any other message passes through untouched.
   */
  rewriteModernTaskResult(message) {
    if (!isModernCreateTaskResult(message.result)) {
      return message;
    }
    const task = message.result;
    return {
      ...message,
      result: {
        resultType: "complete",
        content: [{ type: "text", text: `Modern task ${task.taskId} created` }],
        _meta: { [MODERN_TASK_HANDLE_META]: task }
      }
    };
  }
  /**
   * Send an extension method the SDK v2 era gate refuses to route — the modern
   * `tasks/get` / `tasks/update` / `tasks/cancel`, which are spec-method names
   * absent from the 2026-07-28 era, so `client.request` throws
   * `MethodNotSupportedByProtocolVersion` before anything reaches the wire.
   *
   * We mint a string JSON-RPC id (the SDK only mints numeric ids, so ours never
   * collide), send the raw frame straight through the transport (which still
   * logs it via `trackRequest`, so the Protocol/Network tabs see it), and await
   * the matching response — captured and consumed by the transport's
   * consume-response hook so it never confuses the SDK Client. The response is
   * validated with the caller's explicit schema.
   */
  async rawWireRequest(method, params, resultSchema) {
    const transport = this.transport;
    if (!transport) {
      throw new Error("Client is not connected");
    }
    const id = `inspector-ext-${this.rawWireRequestCounter += 1}`;
    const message = {
      jsonrpc: "2.0",
      id,
      method,
      params
    };
    const timeoutMs = this.requestTimeout ?? 3e4;
    const raw = await new Promise((resolve4, reject) => {
      const timer = setTimeout(() => {
        this.pendingRawWireRequests.delete(id);
        reject(
          new Error(`Raw request "${method}" timed out after ${timeoutMs} ms`)
        );
      }, timeoutMs);
      this.pendingRawWireRequests.set(id, { resolve: resolve4, reject, timer });
      transport.send(message).catch((err) => {
        const pending = this.pendingRawWireRequests.get(id);
        if (pending) {
          clearTimeout(pending.timer);
          this.pendingRawWireRequests.delete(id);
        }
        reject(err instanceof Error ? err : new Error(String(err)));
      });
    });
    return resultSchema.parse(raw);
  }
  /**
   * Transport consume-response hook: resolve/reject a pending
   * {@link rawWireRequest} when its response arrives, and report it as consumed
   * (so the transport does not forward it to the SDK Client, which never sent
   * it). Returns false for any id we don't own, leaving normal SDK traffic
   * untouched.
   */
  consumeRawWireResponse(message) {
    const id = String(message.id);
    const pending = this.pendingRawWireRequests.get(id);
    if (!pending) {
      return false;
    }
    this.pendingRawWireRequests.delete(id);
    clearTimeout(pending.timer);
    if ("error" in message) {
      const err = message.error;
      pending.reject(new Error(err?.message ?? `Request ${id} failed`));
    } else {
      pending.resolve(message.result);
    }
    return true;
  }
  /**
   * Reject and clear all pending raw-wire requests — on every route out that
   * can hold one, and at the top of `connect()` for the route in that settles
   * nothing (see the comment there).
   */
  rejectPendingRawWireRequests(reason) {
    for (const [, pending] of this.pendingRawWireRequests) {
      clearTimeout(pending.timer);
      pending.reject(new Error(reason));
    }
    this.pendingRawWireRequests.clear();
  }
  /**
   * Get requestor task status by taskId (tasks we created on the server)
   * @param taskId Task identifier
   * @returns Task status
   */
  async getRequestorTask(taskId) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    if (this.isTasksExtensionNegotiated()) {
      const modern = await this.rawWireRequest(
        "tasks/get",
        this.withModernTaskEnvelope({ taskId }),
        ModernGetTaskResultSchema
      );
      const task2 = normalizeModernTask(modern);
      this.dispatchTypedEvent("requestorTaskUpdated", {
        taskId: task2.taskId,
        task: task2
      });
      return task2;
    }
    const task = await this.client.request(
      { method: "tasks/get", params: { taskId } },
      GetTaskResultSchema,
      this.getRequestOptions()
    );
    this.dispatchTypedEvent("requestorTaskUpdated", {
      taskId: task.taskId,
      task
    });
    return task;
  }
  /**
   * Get requestor task result by taskId (tasks we created on the server)
   * @param taskId Task identifier
   * @returns Task result
   */
  async getRequestorTaskResult(taskId) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    return await this.client.request(
      { method: "tasks/result", params: { taskId } },
      CallToolResultSchema,
      this.getRequestOptions()
    );
  }
  /**
   * Cancel a running requestor task (task we created on the server)
   * @param taskId Task identifier
   * @returns Cancel result
   */
  async cancelRequestorTask(taskId) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    this.cancelledTaskIds.add(taskId);
    const inputAbort = this.taskInputAbortControllers.get(taskId);
    if (inputAbort) {
      inputAbort.abort(new Error(`Task ${taskId} cancelled by user`));
    }
    if (this.isTasksExtensionNegotiated()) {
      await this.rawWireRequest(
        "tasks/cancel",
        this.withModernTaskEnvelope({ taskId }),
        ModernCancelTaskResultSchema
      );
    } else {
      await this.client.request(
        { method: "tasks/cancel", params: { taskId } },
        CancelTaskResultSchema,
        this.getRequestOptions()
      );
    }
    this.dispatchTypedEvent("taskCancelled", { taskId });
  }
  /**
   * Fulfil the outstanding `inputRequests` of a modern (SEP-2663)
   * `input_required` task by sending `tasks/update` with the collected
   * `inputResponses`. The server acks with an empty result; the task's
   * observable status advances on a subsequent `tasks/get` poll (the update is
   * eventually consistent). Modern-only — legacy tasks surface input through the
   * server→client request channel, not `tasks/update`.
   *
   * @param taskId Task identifier
   * @param inputResponses Responses keyed by the server's `inputRequests` ids
   */
  async updateRequestorTask(taskId, inputResponses) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    await this.rawWireRequest(
      "tasks/update",
      this.withModernTaskEnvelope({ taskId, inputResponses }),
      ModernUpdateTaskResultSchema
    );
  }
  /**
   * Cancel the in-flight ordinary (non-task) tool call started by
   * {@link callTool}. Aborting its request makes the SDK send a
   * `notifications/cancelled` to the server (the MCP cancellation flow) and
   * reject the pending call, which `callTool` surfaces as a
   * {@link ToolCallCancelledError}.
   *
   * Task-augmented calls have a server-side task and are cancelled via
   * {@link cancelRequestorTask} instead — this is a no-op for them (and whenever
   * no ordinary call is in flight).
   *
   * @returns `true` if a call was in flight to cancel, `false` otherwise.
   */
  cancelToolCall() {
    const controller = this.activeToolCallAbortController;
    if (!controller) {
      return false;
    }
    this.activeToolCallAbortController = void 0;
    controller.abort(TOOL_CALL_CANCELLED_REASON);
    return true;
  }
  /**
   * List all requestor tasks with optional pagination (tasks we created on the server)
   * @param cursor Optional pagination cursor
   * @returns List of tasks with optional next cursor
   */
  async listRequestorTasks(cursor) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const result = await this.client.request(
      { method: "tasks/list", params: cursor ? { cursor } : {} },
      ListTasksResultSchema,
      this.getRequestOptions()
    );
    return { tasks: result.tasks, nextCursor: result.nextCursor };
  }
  /**
   * Surface a sampling request through the pending-request UI and resolve with
   * the user's answer. Shared by the inbound `sampling/createMessage` handler
   * (legacy server→client request) and the MRTR driver (a modern
   * `input_required` round embeds the request in a tool-call result). `origin`
   * tags which of the two so the UI can show era-accurate semantics.
   *
   * Sampling has no decline/cancel action (unlike elicitation): the panel
   * either sends a `CreateMessageResult` (resolve — echoed to the server) or
   * Rejects (reject — which fails the tool call). A `signal` abort likewise
   * rejects so the MRTR driver can abort the originating call. `signal` is only
   * passed by the MRTR driver (the tool call's abort signal) — while the driver
   * awaits an answer there is no in-flight SDK request to carry it.
   */
  enqueuePendingSample(request, origin, signal) {
    return new Promise((resolvePromise, rejectPromise) => {
      const sample = new SamplingCreateMessage(
        request,
        resolvePromise,
        rejectPromise,
        (id) => this.removePendingSample(id),
        origin
      );
      this.addPendingSample(sample);
      this.wirePendingAbort(signal, () => {
        this.removePendingSample(sample.id);
        rejectPromise(createPendingAbortError());
      });
    });
  }
  /**
   * Surface an elicitation request through the pending-request UI and resolve
   * with the user's answer. Shared by the inbound `elicitation/create` handler
   * and the MRTR driver — see {@link enqueuePendingSample} for the `origin` /
   * `signal` semantics. A declined/cancelled elicitation resolves with the
   * corresponding `ElicitResult` (echoed to the server on retry); only a
   * genuine failure or a `signal` abort rejects.
   */
  enqueuePendingElicitation(request, origin, signal) {
    return new Promise((resolvePromise, rejectPromise) => {
      const elicitation = new ElicitationCreateMessage(
        request,
        resolvePromise,
        (id) => this.removePendingElicitation(id),
        rejectPromise,
        origin
      );
      this.addPendingElicitation(elicitation);
      this.wirePendingAbort(signal, () => {
        this.removePendingElicitation(elicitation.id);
        rejectPromise(createPendingAbortError());
      });
    });
  }
  /**
   * Reject a still-pending request when `signal` aborts (e.g. the user cancels
   * the tool call while its MRTR round is awaiting an answer). No-op when
   * `signal` is absent — the legacy inbound-handler path passes none.
   */
  wirePendingAbort(signal, onAbort) {
    if (!signal) return;
    if (signal.aborted) {
      onAbort();
      return;
    }
    signal.addEventListener("abort", onAbort, { once: true });
  }
  /**
   * Drive a multi-round-trip request (SEP-2322 "MRTR") for one of the modern
   * multi-round-trip methods (`tools/call`, `prompts/get`, `resources/read`).
   *
   * The client is constructed with `inputRequired: { autoFulfill: false }`, so
   * an `input_required` result is handed back here (via `allowInputRequired`)
   * instead of the SDK silently fulfilling and retrying. We surface each
   * embedded request through the SAME pending-request UI the legacy
   * server→client path uses (`fulfilInputRequests`), gather the bare results,
   * and retry the ORIGINAL request with `inputResponses` + the echoed
   * `requestState` on a fresh JSON-RPC id (`client.request` mints it). The loop
   * runs until the server returns a complete result, bounded by
   * {@link MRTR_MAX_ROUNDS}.
   *
   * On legacy connections a server never returns `input_required`, so the first
   * response is always complete and this is a single `client.request` call.
   */
  async requestWithInputRequired(method, params, resultSchema, requestOptions) {
    const client = this.client;
    if (!client) {
      throw new Error("Client is not connected");
    }
    const wrapped = withInputRequired(resultSchema);
    const signal = requestOptions.signal;
    let round = 0;
    let nextParams = params;
    while (true) {
      const outcome = await client.request(
        { method, params: nextParams },
        wrapped,
        {
          ...requestOptions,
          allowInputRequired: true
        }
      );
      if (!isInputRequiredResult(outcome)) {
        return outcome;
      }
      round += 1;
      if (round > _InspectorClient.MRTR_MAX_ROUNDS) {
        throw new Error(
          `Multi-round-trip "${method}" exceeded ${_InspectorClient.MRTR_MAX_ROUNDS} input_required rounds without completing.`
        );
      }
      const inputResponses = await this.fulfilInputRequests(
        outcome.inputRequests,
        signal
      );
      nextParams = {
        ...params,
        ...inputResponses ? { inputResponses } : {},
        ...outcome.requestState !== void 0 ? { requestState: outcome.requestState } : {}
      };
    }
  }
  /**
   * Fulfil the embedded requests of one MRTR `input_required` round, keyed by
   * the server-assigned identifiers echoed back in `inputResponses`. Sequential
   * (one modal at a time) to keep the single-slot pending UI coherent. Returns
   * `undefined` for a `requestState`-only round (no embedded requests); an empty
   * `inputRequests` map yields an empty `{}`, which the retry echoes harmlessly.
   */
  async fulfilInputRequests(inputRequests, signal, origin = "input-required") {
    if (!inputRequests) return void 0;
    const responses = {};
    for (const [key, embedded] of Object.entries(inputRequests)) {
      responses[key] = await this.fulfilEmbeddedInputRequest(
        embedded,
        signal,
        origin
      );
    }
    return responses;
  }
  /**
   * Fulfil a single embedded input request. `roots/list` is auto-answered from
   * the configured roots (consistent with the legacy `roots/list` handler — no
   * pending UX); `elicitation/create` and `sampling/createMessage` surface
   * through the pending-request UI tagged with `origin`. `origin` distinguishes
   * an MRTR round (`"input-required"`, answer echoed as a retry) from a modern
   * task round (`"task-input-required"`, answer submitted via `tasks/update`).
   */
  async fulfilEmbeddedInputRequest(request, signal, origin = "input-required") {
    switch (request.method) {
      case "roots/list":
        return { roots: this.roots ?? [] };
      case "elicitation/create":
        return this.enqueuePendingElicitation(request, origin, signal);
      case "sampling/createMessage":
        return this.enqueuePendingSample(request, origin, signal);
      /* v8 ignore next 6 -- defensive: an SDK server rejects an unknown embedded
         method before it reaches the wire, so this only guards against a
         non-conformant hand-rolled server; not reproducible against the
         SDK-based test servers. */
      default:
        throw new Error(
          `Unsupported embedded input_required request method: ${request.method}`
        );
    }
  }
  /**
   * Get all pending sampling requests
   */
  getPendingSamples() {
    return [...this.pendingSamples];
  }
  /**
   * Add a pending sampling request
   */
  addPendingSample(sample) {
    this.pendingSamples.push(sample);
    this.dispatchTypedEvent("pendingSamplesChange", this.pendingSamples);
    this.dispatchTypedEvent("newPendingSample", sample);
  }
  /**
   * Remove a pending sampling request by ID
   */
  removePendingSample(id) {
    const index = this.pendingSamples.findIndex((s) => s.id === id);
    if (index !== -1) {
      this.pendingSamples.splice(index, 1);
      this.dispatchTypedEvent("pendingSamplesChange", this.pendingSamples);
    }
  }
  /**
   * Get all pending elicitation requests
   */
  getPendingElicitations() {
    return [...this.pendingElicitations];
  }
  /**
   * Add a pending elicitation request
   */
  addPendingElicitation(elicitation) {
    this.pendingElicitations.push(elicitation);
    this.dispatchTypedEvent(
      "pendingElicitationsChange",
      this.pendingElicitations
    );
    this.dispatchTypedEvent("newPendingElicitation", elicitation);
  }
  /**
   * Remove a pending elicitation request by ID
   */
  removePendingElicitation(id) {
    const index = this.pendingElicitations.findIndex((e) => e.id === id);
    if (index !== -1) {
      this.pendingElicitations.splice(index, 1);
      this.dispatchTypedEvent(
        "pendingElicitationsChange",
        this.pendingElicitations
      );
    }
  }
  /**
   * Get server capabilities
   */
  getCapabilities() {
    return this.capabilities;
  }
  /**
   * Get the capabilities this client advertises to the server. Snapshotted
   * from the initialize-time build in setupClient(); does not reflect later
   * registerCapabilities() calls on the underlying SDK Client.
   */
  getClientCapabilities() {
    return this.clientCapabilities;
  }
  /**
   * Get server info (name, version)
   */
  getServerInfo() {
    return this.serverInfo;
  }
  /**
   * Get server instructions
   */
  getInstructions() {
    return this.instructions;
  }
  /**
   * Get the MCP protocol version negotiated with the server. On a legacy
   * connect this is the version from the initialize handshake (e.g.
   * "2025-06-18"); on a modern connect it's the negotiated modern revision.
   * Undefined when not connected.
   */
  getProtocolVersion() {
    return this.protocolVersion;
  }
  /**
   * The protocol era negotiated with the server (SEP §7.8): `"legacy"` for the
   * 2025-11-25 initialize handshake, `"modern"` for the 2026-era sessionless
   * model. Populated for every era once connected — including a plain legacy
   * (`mode: "legacy"`) connect, which the SDK reports as `"legacy"`. Undefined
   * only when not connected (before connect / after disconnect). (#1626)
   */
  getProtocolEra() {
    return this.protocolEra;
  }
  /**
   * The `server/discover` result captured on a probed (`"auto"`) or pinned
   * (`"modern"`) connect — server identity, capabilities, and supported
   * versions learned up front without an initialize handshake. Undefined when
   * not connected or on a plain legacy connect. Persistable and feedable back
   * to the SDK as `connect(transport, { prior })` for a zero-round-trip
   * reconnect. (#1626)
   */
  getDiscoverResult() {
    return this.discoverResult;
  }
  /**
   * The per-server settings this client was constructed with (headers,
   * timeouts, roots, OAuth, the auto-refresh-on-list-changed option, etc.).
   * Read by the managed list state to decide whether to auto-refresh on
   * `list_changed` notifications (#1402).
   */
  getServerSettings() {
    return this.serverSettings;
  }
  /**
   * Replace the in-memory per-server settings on a live client. Lets a settings
   * edit (e.g. toggling auto-refresh-on-list-changed) take effect on the
   * current connection without a reconnect — the managed list state reads
   * `getServerSettings()` at notification time, so the next `list_changed`
   * notification honors the new value (#1444). Connection-time inputs
   * (transport, OAuth, timeouts) still only apply on the next connect.
   */
  setServerSettings(settings) {
    this.serverSettings = settings;
  }
  /**
   * Set the logging level for the MCP server (legacy era only).
   *
   * On legacy servers logging is session-scoped: one `logging/setLevel` request
   * sets the level for all subsequent `notifications/message`. Modern servers
   * removed this method — use {@link setModernLogLevel} there instead.
   *
   * @param level Logging level to set
   * @throws Error if client is not connected or server doesn't support logging
   */
  async setLoggingLevel(level) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    if (!this.capabilities?.logging) {
      throw new Error("Server does not support logging");
    }
    await this.client.setLoggingLevel(level, this.getRequestOptions());
  }
  /**
   * Set (or clear) the modern-era per-request log level (#1629).
   *
   * On 2026-07-28 servers `logging/setLevel` is gone and there is no
   * session-scoped level: the client opts into logs per request by stamping the
   * `io.modelcontextprotocol/logLevel` `_meta` key, and the server MUST NOT emit
   * `notifications/message` for requests that omit it. This stores the level so
   * {@link mergeMeta} stamps it on every subsequent request; pass `undefined` to
   * stop opting in (logs then stay silently absent). Takes effect immediately —
   * no request is sent, and it is a no-op on the wire until the next request.
   *
   * @param level Level to stamp on every request, or `undefined` to opt out.
   */
  setModernLogLevel(level) {
    this.modernLogLevel = level;
  }
  /** The modern-era per-request log level, or `undefined` when not opted in. */
  getModernLogLevel() {
    return this.modernLogLevel;
  }
  /**
   * Fetch a single page of tools without updating the client's internal list.
   */
  async listTools(cursor, metadata) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const effectiveMeta = this.mergeMeta(metadata);
    const params = {
      ...effectiveMeta ? { _meta: effectiveMeta } : {},
      ...cursor ? { cursor } : {}
    };
    const response = await this.invokeMcpClient(
      () => this.client.request(
        { method: "tools/list", params },
        ListToolsResultSchema,
        this.getRequestOptions(metadata?.progressToken)
      )
    );
    const tools = [...response.tools || []];
    return { tools, nextCursor: response.nextCursor };
  }
  /**
   * Aggregate ALL pages of `tools/list` via the SDK's high-level
   * `client.listTools()` — the cache-aware verb. Unlike the single-page
   * {@link listTools} (raw `client.request`, for pagination debugging), this is
   * the path the managed tool list uses on refresh: the SDK walks every page,
   * applies the SEP-2243 `x-mcp-header` exclusion, and consults/writes its
   * response cache. `cacheMode` selects the disposition (`'use'` serves a
   * still-fresh cached list without a round trip; `'refresh'` always fetches
   * and re-stores; `'bypass'` fetches without touching the cache) — only
   * meaningful on modern servers that send `ttlMs` hints; a no-op on legacy
   * (nothing is cached, so every call hits the wire regardless).
   */
  async listAllTools(options) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const response = await this.invokeMcpClient(
      () => this.client.listTools(
        this.aggregateListParams(options?.metadata),
        this.getCacheableRequestOptions(options?.cacheMode)
      )
    );
    await this.refreshExcludedTools(options?.metadata).catch((err) => {
      this.logger.warn(
        { err },
        "Excluded-tools walk failed; the SEP-2243 excluded list may be incomplete"
      );
    });
    return { tools: [...response.tools] };
  }
  /**
   * Whether this connection excludes tools with invalid `x-mcp-header`
   * annotations from `tools/list`, matching the SDK's gate: only the modern
   * (2026-07-28) era on a non-stdio (Streamable HTTP / SSE) transport. Legacy
   * and stdio keep such tools in the list, so there is nothing to surface.
   */
  excludesInvalidXMcpHeaderTools() {
    return this.isModernEra() && this.getServerType() !== "stdio";
  }
  /**
   * Mark the response that most recently answered `method` as rejected by the
   * client, so its Protocol entry shows why instead of rendering as a clean
   * success (#1953).
   *
   * The SDK gives no request id with a decode failure — `SdkError` carries the
   * method and nothing else — so the id is recovered by correlation: the last
   * response received for that method. That is exact rather than approximate,
   * because the SDK rejects synchronously while decoding the response (inside
   * the transport's `onmessage`) and the caller's `catch` runs in the very next
   * microtask. Delivering another response for the same method in that window
   * would take a macrotask (a socket read), which cannot interleave there.
   *
   * A no-op when nothing has answered `method` this session.
   */
  markResponseRejected(method, reason) {
    const id = this.lastAnsweredRequestByMethod.get(method);
    if (id === void 0) return;
    this.dispatchTypedEvent("responseRejected", { id, reason });
  }
  /** The current SEP-2243 excluded-tools set (empty on legacy/stdio). */
  getExcludedTools() {
    return this.excludedTools;
  }
  /**
   * Recompute the tools the SDK excludes from `tools/list` for invalid
   * `x-mcp-header` annotations (SEP-2243), and emit `excludedToolsChange`.
   * Returns `[]` without any round trip on connections that don't exclude
   * (legacy/stdio). Otherwise walks every page of the RAW `tools/list` (which,
   * unlike the SDK's high-level `listTools()`, is NOT filtered) and keeps the
   * tools whose annotation scan fails, each with its reason. A repeating cursor
   * stops the walk (non-converging-server guard, mirroring the SDK).
   */
  async refreshExcludedTools(metadata) {
    const excluded = [];
    if (this.excludesInvalidXMcpHeaderTools()) {
      const seenCursors = /* @__PURE__ */ new Set();
      let cursor;
      do {
        const page = await this.listTools(cursor, metadata);
        for (const tool of page.tools) {
          const scan = scanXMcpHeaderDeclarations(tool.inputSchema);
          if (!scan.valid) excluded.push({ tool, reason: scan.reason });
        }
        cursor = page.nextCursor;
        if (cursor !== void 0) {
          if (seenCursors.has(cursor)) break;
          seenCursors.add(cursor);
        }
      } while (cursor !== void 0);
    }
    this.excludedTools = excluded;
    this.dispatchTypedEvent("excludedToolsChange", excluded);
    return excluded;
  }
  /**
   * Call a tool. Caller must provide the Tool (e.g. from a state manager).
   * @param tool The tool to call (use tool.name for the request)
   * @param args Tool arguments
   * @param generalMetadata Optional general metadata
   * @param toolSpecificMetadata Optional tool-specific metadata (takes precedence over general)
   * @param taskOptions Optional task options (e.g. ttl) for task-augmented requests
   * @returns Tool call response
   */
  async callTool(tool, args, generalMetadata, toolSpecificMetadata, taskOptions, options) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    if (tool.execution?.taskSupport === "required") {
      throw new Error(
        `Tool "${tool.name}" requires task support. Use callToolStream() instead of callTool().`
      );
    }
    const request = {
      tool,
      args,
      generalMetadata,
      toolSpecificMetadata,
      taskOptions,
      options
    };
    const abortController = new AbortController();
    this.activeToolCallAbortController = abortController;
    try {
      return await this.callToolWithRetries(request, abortController);
    } finally {
      if (this.activeToolCallAbortController === abortController) {
        this.activeToolCallAbortController = void 0;
      }
    }
  }
  /**
   * The URL-elicitation retry loop for {@link callTool}, factored out so the
   * caller can wrap it in the abort-controller lifecycle (`try`/`finally`). On a
   * user cancellation (the call's abort signal fired with the cancel reason) the
   * SDK has already sent `notifications/cancelled`, so we throw a
   * {@link ToolCallCancelledError} without recording a failed call — the cancel
   * was intentional, not a failure.
   */
  async callToolWithRetries(request, abortController) {
    const { tool, args, generalMetadata, toolSpecificMetadata } = request;
    let urlElicitationAttempt = 0;
    const presentedUrls = /* @__PURE__ */ new Set();
    while (true) {
      try {
        return await this.attemptToolCall(request, abortController.signal);
      } catch (error) {
        if (abortController.signal.aborted && abortController.signal.reason === TOOL_CALL_CANCELLED_REASON) {
          throw new ToolCallCancelledError(tool.name);
        }
        const urlElicitations = getUrlElicitationsFromError(error);
        if (urlElicitations && urlElicitations.length > 0 && urlElicitationAttempt < MAX_URL_ELICITATION_RETRIES) {
          const repeated = urlElicitations.find(
            (e) => presentedUrls.has(e.url)
          );
          if (repeated) {
            const loopError = new UrlElicitationLoopError(repeated.url);
            this.dispatchFailedToolCall(
              tool,
              args,
              generalMetadata,
              toolSpecificMetadata,
              loopError.message
            );
            throw loopError;
          }
          urlElicitationAttempt++;
          for (const e of urlElicitations) {
            presentedUrls.add(e.url);
          }
          const action = await this.runUrlElicitations(urlElicitations);
          if (action === "accept") {
            continue;
          }
          const abortError = new Error(
            `Tool call cancelled: required URL elicitation was ${action === "decline" ? "declined" : "cancelled"}.`
          );
          this.dispatchFailedToolCall(
            tool,
            args,
            generalMetadata,
            toolSpecificMetadata,
            abortError.message
          );
          throw abortError;
        }
        if (urlElicitations && urlElicitations.length > 0) {
          this.logger.warn(
            { tool: tool.name, attempts: urlElicitationAttempt },
            `Tool "${tool.name}" still required URL elicitations after ${MAX_URL_ELICITATION_RETRIES} attempts; giving up.`
          );
        }
        this.dispatchFailedToolCall(
          tool,
          args,
          generalMetadata,
          toolSpecificMetadata,
          error instanceof Error ? error.message : String(error)
        );
        throw error;
      }
    }
  }
  /**
   * Coerce the string-valued entries of a tool's arguments to the types its
   * `inputSchema` declares (the Tools form hands everything over as text).
   * Shared by the two `tools/call` entry points — {@link attemptToolCall} and
   * {@link callToolStream} — so both put the SAME arguments on the wire.
   */
  convertStringToolArgs(tool, args) {
    const stringArgs = {};
    for (const [key, value] of Object.entries(args)) {
      if (typeof value === "string") {
        stringArgs[key] = value;
      }
    }
    if (Object.keys(stringArgs).length === 0) return args;
    return { ...args, ...convertToolParameters(tool, stringArgs) };
  }
  /**
   * SEP-2243: mirror `x-mcp-header`-annotated arguments into `Mcp-Param-*`
   * headers on a modern connection. The SDK only does this inside
   * `client.callTool()` (and skips it in the browser), but we route
   * `tools/call` through `client.request()` for manual MRTR driving (#1704), so
   * we mirror ourselves. `Protocol.request` forwards `headers` (preserved
   * across MRTR retry legs) to the transport, and the remote transport relays
   * them to the backend's upstream send — issued server-side, where the browser
   * skip doesn't apply. No-op on legacy/stdio (no annotations).
   *
   * Applied by BOTH `tools/call` entry points: a plain call
   * ({@link attemptToolCall}) and a task-augmented one
   * ({@link callToolStream}) — a strict modern server rejects either with
   * `-32020` when the mirrored header is missing.
   */
  applyMirroredParamHeaders(tool, convertedArgs, requestOptions) {
    if (this.protocolEra !== "modern") return;
    const paramHeaders = mcpParamHeadersForTool(tool, convertedArgs);
    if (Object.keys(paramHeaders).length === 0) return;
    requestOptions.headers = { ...requestOptions.headers, ...paramHeaders };
  }
  /**
   * Run a single tools/call attempt: convert args, issue the request, validate,
   * and return a successful {@link ToolCallInvocation}. Throws on any error
   * (including a `-32042` UrlElicitationRequired response); {@link callTool}'s
   * retry loop owns the elicitation handling and failure bookkeeping.
   */
  async attemptToolCall(request, signal) {
    const {
      tool,
      args,
      generalMetadata,
      toolSpecificMetadata,
      taskOptions,
      options
    } = request;
    const client = this.client;
    if (!client) {
      throw new Error("Client is not connected");
    }
    const convertedArgs = this.convertStringToolArgs(tool, args);
    const callMetadata = generalMetadata || toolSpecificMetadata ? { ...generalMetadata || {}, ...toolSpecificMetadata || {} } : void 0;
    const timestamp = /* @__PURE__ */ new Date();
    const metadata = this.mergeMeta(callMetadata);
    const callParams = {
      name: tool.name,
      arguments: convertedArgs,
      _meta: metadata
    };
    if (taskOptions?.ttl != null) {
      callParams.task = { ttl: taskOptions.ttl };
    }
    const requestOptions = this.getRequestOptions(
      metadata?.progressToken,
      signal
    );
    this.applyMirroredParamHeaders(tool, convertedArgs, requestOptions);
    const rawResult = await this.invokeMcpClient(
      () => this.requestWithInputRequired(
        "tools/call",
        callParams,
        CallToolResultSchema,
        requestOptions
      ),
      { method: "tools/call", toolName: tool.name }
    );
    const taskHandle = rawResult._meta?.[MODERN_TASK_HANDLE_META];
    const result = taskHandle ? await this.pollModernTaskToTermination(taskHandle) : rawResult;
    const outputValidationError = this.validateToolOutput(tool, result);
    if (outputValidationError && !options?.skipOutputValidation) {
      throw new ProtocolError2(
        ProtocolErrorCode2.InvalidParams,
        outputValidationError
      );
    }
    const invocation = {
      toolName: tool.name,
      params: args,
      result,
      timestamp,
      success: true,
      metadata,
      outputValidationError
    };
    this.dispatchTypedEvent("toolCallResultChange", {
      toolName: tool.name,
      params: args,
      result: invocation.result,
      timestamp,
      success: true,
      metadata,
      outputValidationError
    });
    return invocation;
  }
  /**
   * Record a failed tools/call as a `toolCallResultChange` event (history + the
   * Tools panel) without throwing. {@link callTool} calls this before rethrowing
   * so a failure — whether a transport error, a declined URL elicitation, or a
   * non-spec `-32042` — lands in the request history exactly once.
   */
  dispatchFailedToolCall(tool, args, generalMetadata, toolSpecificMetadata, errorMessage) {
    const callMetadata = generalMetadata || toolSpecificMetadata ? { ...generalMetadata || {}, ...toolSpecificMetadata || {} } : void 0;
    const metadata = this.mergeMeta(callMetadata);
    this.dispatchTypedEvent("toolCallResultChange", {
      toolName: tool.name,
      params: args,
      result: null,
      timestamp: /* @__PURE__ */ new Date(),
      success: false,
      error: errorMessage,
      metadata
    });
  }
  /**
   * Surface the URL elicitations carried by a `-32042` error, one at a time and
   * in order (per the spec's "URL mode with elicitation required error" flow),
   * returning as soon as the user declines/cancels one. Returns `"accept"` only
   * when every elicitation was accepted, which is {@link callTool}'s signal to
   * retry the original call.
   */
  async runUrlElicitations(elicitations) {
    for (const params of elicitations) {
      const action = await this.awaitUrlElicitation(params);
      if (action !== "accept") {
        return action;
      }
    }
    return "accept";
  }
  /**
   * Add one error-path URL elicitation to the pending queue (so it renders in
   * the same modal as request-path elicitations) and resolve with the user's
   * action. Unlike the request-path handler there is no server request to
   * answer — accepting it just unblocks the retry; the server's optional
   * `notifications/elicitation/complete` resolves it as accepted too (via
   * `completeIfPending`).
   */
  awaitUrlElicitation(params) {
    return new Promise((resolve4) => {
      const request = {
        method: "elicitation/create",
        params
      };
      const message = new ElicitationCreateMessage(
        request,
        (result) => resolve4(result.action),
        (id) => this.removePendingElicitation(id)
      );
      this.addPendingElicitation(message);
    });
  }
  /**
   * Non-fatally validate a delivered tool result against the tool's outputSchema
   * (used by the skipOutputValidation path). Delegates to the pure
   * {@link validateToolOutput} helper with this client's lazily-built Ajv
   * validator. Returns an advisory message, or undefined when valid.
   */
  validateToolOutput(tool, result) {
    this.outputValidator ??= new AjvJsonSchemaValidator();
    return validateToolOutput(this.outputValidator, tool, result);
  }
  /**
   * When a modern (SEP-2663) task is `input_required`, fulfil its embedded
   * `inputRequests` through the pending-request UI and submit them via
   * `tasks/update`. No-op for any other status. Shared by the streaming
   * ({@link pollTaskToolCall}) and ordinary ({@link pollModernTaskToTermination})
   * poll loops so the input handling lives in one place.
   *
   * `priorRounds` is the count of `input_required` rounds already handled for
   * this task; the return value is the updated count. A non-conformant server
   * that keeps returning `input_required` without ever completing would
   * otherwise re-prompt the user on every poll forever, so we bound it with the
   * same {@link MRTR_MAX_ROUNDS} cap the MRTR driver uses.
   */
  async submitModernTaskInput(detailed, task, priorRounds, signal) {
    if (task.status !== "input_required") {
      return priorRounds;
    }
    const rounds = priorRounds + 1;
    if (rounds > _InspectorClient.MRTR_MAX_ROUNDS) {
      throw new Error(
        `Modern task "${task.taskId}" exceeded ${_InspectorClient.MRTR_MAX_ROUNDS} input_required rounds without completing.`
      );
    }
    const inputResponses = await this.fulfilInputRequests(
      this.tagInputRequestsWithTask(readInputRequests(detailed), task.taskId),
      signal,
      "task-input-required"
    );
    if (inputResponses) {
      await this.updateRequestorTask(task.taskId, inputResponses);
    }
    return rounds;
  }
  /**
   * Stamp `_meta[RELATED_TASK_META_KEY]` with the owning task id on each embedded
   * request of a modern task's `inputRequests`. The pending-request UI reads that
   * id (via `ElicitationCreateMessage.taskId`) so its Cancel control can cancel
   * the TASK — not just answer the request — when a task is paused at
   * `input_required`.
   */
  tagInputRequestsWithTask(inputRequests, taskId) {
    if (!inputRequests) return inputRequests;
    const tagged = {};
    for (const [key, req] of Object.entries(inputRequests)) {
      const request = req;
      tagged[key] = {
        ...request,
        params: {
          ...request.params,
          _meta: {
            ...request.params?._meta,
            [RELATED_TASK_META_KEY3]: { taskId }
          }
        }
      };
    }
    return tagged;
  }
  /**
   * Terminal outcome for a modern task: the inlined `CallToolResult` for a
   * `completed` task (SEP-2663 removed the blocking `tasks/result`), or a
   * `ProtocolError` for `failed` / `cancelled`. Shared so both poll loops agree
   * on the result/error shape.
   */
  modernTaskTerminalOutcome(task, detailed) {
    if (task.status === "completed") {
      return {
        type: "result",
        result: detailed.result ?? { content: [] }
      };
    }
    return {
      type: "error",
      error: new ProtocolError2(
        ProtocolErrorCode2.InternalError,
        task.statusMessage ?? `Task ${task.status}`
      )
    };
  }
  /**
   * Poll cadence for a task: the server-advertised `pollInterval` when
   * positive, else the default. Shared by every task poll loop (both eras).
   */
  taskPollInterval(task) {
    const advertised = task.pollInterval;
    if (typeof advertised !== "number") return DEFAULT_TASK_POLL_INTERVAL_MS;
    return advertised > 0 ? advertised : DEFAULT_TASK_POLL_INTERVAL_MS;
  }
  /**
   * Register a per-task abort controller (keyed by taskId) whose signal gates
   * the task's `input_required` pending request, and return the signal plus a
   * `release` cleanup. {@link cancelRequestorTask} aborts it to unblock a task
   * paused at the pending-request modal.
   */
  registerTaskInputAbort(taskId) {
    const controller = new AbortController();
    this.taskInputAbortControllers.set(taskId, controller);
    return {
      signal: controller.signal,
      release: () => {
        if (this.taskInputAbortControllers.get(taskId) === controller) {
          this.taskInputAbortControllers.delete(taskId);
        }
      }
    };
  }
  /**
   * Drive a modern (SEP-2663) task to a terminal state from a seed
   * `DetailedTask`, dispatching task events so the Tasks tab and toasts track
   * it, and return the completed task's inlined `CallToolResult` (or throw on
   * `failed` / `cancelled`). Used by the ORDINARY `callTool` path when a server
   * returns an unsolicited task handle (the run-as-task streaming path drives
   * the equivalent loop inline in {@link pollTaskToolCall}). `input_required`
   * rounds are answered through the pending-request UI and submitted via
   * `tasks/update`.
   */
  async pollModernTaskToTermination(seed) {
    let detailed = seed;
    let task = normalizeModernTask(detailed);
    const emit = (t) => {
      this.dispatchTypedEvent("toolCallTaskUpdated", {
        taskId: t.taskId,
        task: t
      });
      this.dispatchTypedEvent("requestorTaskUpdated", {
        taskId: t.taskId,
        task: t
      });
    };
    emit(task);
    const { signal: inputSignal, release } = this.registerTaskInputAbort(
      task.taskId
    );
    try {
      let inputRounds = 0;
      while (!_InspectorClient.isTerminalTaskStatus(task.status)) {
        inputRounds = await this.submitModernTaskInput(
          detailed,
          task,
          inputRounds,
          inputSignal
        );
        await new Promise(
          (resolve4) => setTimeout(resolve4, this.taskPollInterval(task))
        );
        detailed = await this.rawWireRequest(
          "tasks/get",
          this.withModernTaskEnvelope({ taskId: task.taskId }),
          ModernGetTaskResultSchema
        );
        task = normalizeModernTask(detailed);
        emit(task);
      }
    } finally {
      release();
    }
    const outcome = this.modernTaskTerminalOutcome(task, detailed);
    if (outcome.type === "error") {
      throw outcome.error;
    }
    return outcome.result;
  }
  /**
   * Poll a task-augmented tool call to completion. Replaces the removed
   * `client.experimental.tasks.callToolStream` helper: it sends the
   * task-augmented `tools/call` (the server responds with a task handle, i.e. a
   * `CreateTaskResult`), then polls `tasks/get` until the task reaches a
   * terminal status, yielding the same `taskCreated | taskStatus | result |
   * error` message shapes the caller's `for await` loop consumes — so all the
   * downstream event dispatch and terminal-state handling stays unchanged.
   */
  async *pollTaskToolCall(params, requestOptions) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const client = this.client;
    const progressHandlers = client._progressHandlers;
    const keysBeforeRequest = new Set(progressHandlers.keys());
    const modernTasks = this.isTasksExtensionNegotiated();
    const requestPromise = client.request(
      {
        // On modern the SDK codec stamps the tasks-extension client capability
        // into the request envelope (advertised at construction), so a server
        // may answer with a `CreateTaskResult` — no per-call `_meta` needed.
        method: "tools/call",
        params
      },
      // Modern: the SDK codec can't decode a `resultType: "task"` result, so the
      // transport rewrote it to a `CallToolResult` carrying the task handle in
      // `_meta` — parse as a CallToolResult and read the handle below. Legacy:
      // accept a `{ task }` handle or an immediate result.
      modernTasks ? CallToolResultSchema : CreateTaskResultSchema.or(CallToolResultSchema),
      requestOptions
    );
    const progressSubscriptionId = requestOptions.onprogress ? [...progressHandlers.keys()].find((k) => !keysBeforeRequest.has(k)) : void 0;
    const created = await requestPromise;
    if (modernTasks) {
      const handle = created._meta?.[MODERN_TASK_HANDLE_META];
      if (!handle) {
        yield { type: "result", result: created };
        return;
      }
      let detailed = handle;
      let task2 = normalizeModernTask(detailed);
      yield { type: "taskCreated", task: task2 };
      if (progressSubscriptionId != null && requestOptions.onprogress) {
        progressHandlers.set(progressSubscriptionId, requestOptions.onprogress);
      }
      const { signal: inputSignal, release } = this.registerTaskInputAbort(
        task2.taskId
      );
      let inputRounds = 0;
      try {
        while (!_InspectorClient.isTerminalTaskStatus(task2.status)) {
          inputRounds = await this.submitModernTaskInput(
            detailed,
            task2,
            inputRounds,
            inputSignal
          );
          await new Promise(
            (resolve4) => setTimeout(resolve4, this.taskPollInterval(task2))
          );
          detailed = await this.rawWireRequest(
            "tasks/get",
            this.withModernTaskEnvelope({ taskId: task2.taskId }),
            ModernGetTaskResultSchema
          );
          task2 = normalizeModernTask(detailed);
          yield { type: "taskStatus", task: task2 };
        }
      } finally {
        release();
        if (progressSubscriptionId != null) {
          progressHandlers.delete(progressSubscriptionId);
        }
      }
      yield this.modernTaskTerminalOutcome(task2, detailed);
      return;
    }
    if (!("task" in created) || created.task == null) {
      yield { type: "result", result: created };
      return;
    }
    let task = created.task;
    yield { type: "taskCreated", task };
    if (progressSubscriptionId != null && requestOptions.onprogress) {
      progressHandlers.set(progressSubscriptionId, requestOptions.onprogress);
    }
    try {
      while (!_InspectorClient.isTerminalTaskStatus(task.status)) {
        await new Promise(
          (resolve4) => setTimeout(resolve4, this.taskPollInterval(task))
        );
        task = await client.request(
          { method: "tasks/get", params: { taskId: task.taskId } },
          GetTaskResultSchema,
          this.getRequestOptions()
        );
        yield { type: "taskStatus", task };
      }
    } finally {
      if (progressSubscriptionId != null) {
        progressHandlers.delete(progressSubscriptionId);
      }
    }
    if (task.status === "completed") {
      const result = await client.request(
        { method: "tasks/result", params: { taskId: task.taskId } },
        CallToolResultSchema,
        this.getRequestOptions()
      );
      yield { type: "result", result };
    } else {
      yield {
        type: "error",
        error: new ProtocolError2(
          ProtocolErrorCode2.InternalError,
          task.statusMessage ?? `Task ${task.status}`
        )
      };
    }
  }
  /**
   * Call a tool with task support (streaming).
   * Caller must provide the Tool (e.g. from a state manager).
   * @param tool The tool to call (use tool.name for the request)
   * @param args Tool arguments
   * @param generalMetadata Optional general metadata
   * @param toolSpecificMetadata Optional tool-specific metadata (takes precedence over general)
   * @param taskOptions Optional task options (e.g. ttl) for task-augmented requests
   * @returns Tool call response
   */
  async callToolStream(tool, args, generalMetadata, toolSpecificMetadata, taskOptions) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    try {
      const convertedArgs = this.convertStringToolArgs(tool, args);
      const callMetadata = generalMetadata || toolSpecificMetadata ? { ...generalMetadata || {}, ...toolSpecificMetadata || {} } : void 0;
      const timestamp = /* @__PURE__ */ new Date();
      const metadata = this.mergeMeta(callMetadata);
      const streamParams = {
        name: tool.name,
        arguments: convertedArgs
      };
      if (metadata) {
        streamParams._meta = metadata;
      }
      if (taskOptions?.ttl != null) {
        streamParams.task = { ttl: taskOptions.ttl };
      }
      let finalResult;
      let taskId;
      let error;
      const requestOptions = this.getRequestOptions(metadata?.progressToken);
      this.applyMirroredParamHeaders(tool, convertedArgs, requestOptions);
      if (this.progress) {
        const innerOnProgress = requestOptions.onprogress;
        requestOptions.onprogress = (progress) => {
          innerOnProgress?.(progress);
          if (taskId) {
            this.dispatchTypedEvent("requestorTaskProgress", {
              taskId,
              progress
            });
          }
        };
      }
      const stream = this.pollTaskToolCall(
        streamParams,
        requestOptions
      );
      for await (const message of stream) {
        switch (message.type) {
          case "taskCreated":
            taskId = message.task.taskId;
            this.dispatchTypedEvent("toolCallTaskUpdated", {
              taskId: message.task.taskId,
              task: message.task
            });
            this.dispatchTypedEvent("requestorTaskUpdated", {
              taskId: message.task.taskId,
              task: message.task
            });
            break;
          case "taskStatus":
            if (!taskId) {
              taskId = message.task.taskId;
            }
            this.dispatchTypedEvent("toolCallTaskUpdated", {
              taskId: message.task.taskId,
              task: message.task
            });
            this.dispatchTypedEvent("requestorTaskUpdated", {
              taskId: message.task.taskId,
              task: message.task
            });
            break;
          case "result":
            finalResult = message.result;
            if (taskId) {
              const completedTask = {
                taskId,
                ttl: null,
                status: "completed",
                statusMessage: "Task completed",
                lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
              };
              this.dispatchTypedEvent("toolCallTaskUpdated", {
                taskId,
                task: completedTask,
                result: finalResult
              });
              this.dispatchTypedEvent("requestorTaskUpdated", {
                taskId,
                task: completedTask,
                result: finalResult
              });
            }
            break;
          case "error": {
            const errorMessage = message.error.message || "Task execution failed";
            error = new Error(errorMessage);
            if (taskId) {
              const cancelled = this.cancelledTaskIds.has(taskId);
              this.cancelledTaskIds.delete(taskId);
              const terminalTask = {
                taskId,
                ttl: null,
                status: cancelled ? "cancelled" : "failed",
                statusMessage: cancelled ? "Client cancelled task execution." : errorMessage,
                lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
              };
              this.dispatchTypedEvent("toolCallTaskUpdated", {
                taskId,
                task: terminalTask,
                error: message.error
              });
              this.dispatchTypedEvent("requestorTaskUpdated", {
                taskId,
                task: terminalTask,
                error: message.error
              });
            }
            break;
          }
        }
      }
      if (error) {
        throw error;
      }
      if (!finalResult && taskId) {
        try {
          finalResult = await this.client.request(
            { method: "tasks/result", params: { taskId } },
            CallToolResultSchema,
            this.getRequestOptions()
            // no metadata for fallback
          );
        } catch (resultError) {
          throw new Error(
            `Tool call did not return a result: ${resultError instanceof Error ? resultError.message : String(resultError)}`,
            { cause: resultError }
          );
        }
      }
      if (!finalResult) {
        throw new Error("Tool call did not return a result");
      }
      const invocation = {
        toolName: tool.name,
        params: args,
        result: finalResult,
        timestamp,
        success: true,
        metadata
      };
      this.dispatchTypedEvent("toolCallResultChange", {
        toolName: tool.name,
        params: args,
        result: invocation.result,
        timestamp,
        success: true,
        metadata
      });
      return invocation;
    } catch (error) {
      const callMetadata = generalMetadata || toolSpecificMetadata ? { ...generalMetadata || {}, ...toolSpecificMetadata || {} } : void 0;
      const timestamp = /* @__PURE__ */ new Date();
      const metadata = this.mergeMeta(callMetadata);
      this.dispatchTypedEvent("toolCallResultChange", {
        toolName: tool.name,
        params: args,
        result: null,
        timestamp,
        success: false,
        error: error instanceof Error ? error.message : String(error),
        metadata
      });
      throw error;
    }
  }
  /**
   * List available resources with pagination support (stateless; state managers hold the list).
   * @param cursor Optional cursor for pagination
   * @param metadata Optional metadata to include in the request
   * @returns Object containing resources array and optional nextCursor
   */
  async listResources(cursor, metadata) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const effectiveMeta = this.mergeMeta(metadata);
    const params = {
      ...effectiveMeta ? { _meta: effectiveMeta } : {},
      ...cursor ? { cursor } : {}
    };
    const response = await this.invokeMcpClient(
      () => this.client.request(
        { method: "resources/list", params },
        ListResourcesResultSchema,
        this.getRequestOptions(metadata?.progressToken)
      )
    );
    return {
      resources: response.resources || [],
      nextCursor: response.nextCursor
    };
  }
  /**
   * Aggregate ALL pages of `resources/list` via the SDK's high-level
   * cache-aware `client.listResources()`. See {@link listAllTools} for the
   * `cacheMode` semantics; this is the path the managed resource list uses on
   * refresh.
   */
  async listAllResources(options) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const response = await this.invokeMcpClient(
      () => this.client.listResources(
        this.aggregateListParams(options?.metadata),
        this.getCacheableRequestOptions(options?.cacheMode)
      )
    );
    return { resources: [...response.resources] };
  }
  /**
   * Read a resource by URI
   * @param uri Resource URI
   * @param metadata Optional metadata to include in the request
   * @returns Resource content
   */
  async readResource(uri, metadata) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const effectiveMeta = this.mergeMeta(metadata);
    const params = {
      uri,
      ...effectiveMeta ? { _meta: effectiveMeta } : {}
    };
    const result = await this.invokeMcpClient(
      () => this.requestWithInputRequired(
        "resources/read",
        params,
        ReadResourceResultSchema,
        this.getRequestOptions(metadata?.progressToken)
      ),
      { method: "resources/read" }
    );
    const invocation = {
      result,
      timestamp: /* @__PURE__ */ new Date(),
      uri,
      metadata: effectiveMeta
    };
    this.dispatchTypedEvent("resourceContentChange", {
      uri,
      content: invocation,
      timestamp: invocation.timestamp
    });
    return invocation;
  }
  /**
   * Read a resource from a template by expanding the template URI with parameters
   * This encapsulates the business logic of template expansion and associates the
   * loaded resource with its template in InspectorClient state
   * @param templateName The name/ID of the resource template
   * @param params Parameters to fill in the template variables
   * @param metadata Optional metadata to include in the request
   * @returns The resource content along with expanded URI and template name
   * @throws Error if template is not found or URI expansion fails
   */
  async readResourceFromTemplate(uriTemplate, params, metadata) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const uriTemplateString = uriTemplate;
    let expandedUri;
    try {
      const uriTemplate2 = new UriTemplate(uriTemplateString);
      expandedUri = uriTemplate2.expand(params);
    } catch (error) {
      throw new Error(
        `Failed to expand URI template "${uriTemplate}": ${error instanceof Error ? error.message : String(error)}`,
        { cause: error }
      );
    }
    const readInvocation = await this.readResource(expandedUri, metadata);
    const invocation = {
      uriTemplate: uriTemplateString,
      expandedUri,
      result: readInvocation.result,
      timestamp: readInvocation.timestamp,
      params,
      metadata: readInvocation.metadata
    };
    this.dispatchTypedEvent("resourceTemplateContentChange", {
      uriTemplate: uriTemplateString,
      content: invocation,
      params,
      timestamp: invocation.timestamp
    });
    return invocation;
  }
  /**
   * List resource templates with pagination support (stateless; state managers hold the list).
   * @param cursor Optional cursor for pagination
   * @param metadata Optional metadata to include in the request
   * @returns Object containing resourceTemplates array and optional nextCursor
   */
  async listResourceTemplates(cursor, metadata) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const effectiveMeta = this.mergeMeta(metadata);
    const params = {
      ...effectiveMeta ? { _meta: effectiveMeta } : {},
      ...cursor ? { cursor } : {}
    };
    const response = await this.invokeMcpClient(
      () => this.client.request(
        { method: "resources/templates/list", params },
        ListResourceTemplatesResultSchema,
        this.getRequestOptions(metadata?.progressToken)
      ),
      { method: "resources/templates/list" }
    );
    return {
      resourceTemplates: response.resourceTemplates || [],
      nextCursor: response.nextCursor
    };
  }
  /**
   * Aggregate ALL pages of `resources/templates/list` via the SDK's high-level
   * cache-aware `client.listResourceTemplates()`. See {@link listAllTools} for
   * the `cacheMode` semantics; this is the path the managed resource-template
   * list uses on refresh.
   */
  async listAllResourceTemplates(options) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const response = await this.invokeMcpClient(
      () => this.client.listResourceTemplates(
        this.aggregateListParams(options?.metadata),
        this.getCacheableRequestOptions(options?.cacheMode)
      ),
      { method: "resources/templates/list" }
    );
    return { resourceTemplates: [...response.resourceTemplates] };
  }
  /**
   * List available prompts with pagination support
   * @param cursor Optional cursor for pagination
   * @param metadata Optional metadata to include in the request
   * @returns Object containing prompts array and optional nextCursor
   */
  async listPrompts(cursor, metadata) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const effectiveMeta = this.mergeMeta(metadata);
    const params = {
      ...effectiveMeta ? { _meta: effectiveMeta } : {},
      ...cursor ? { cursor } : {}
    };
    const response = await this.invokeMcpClient(
      () => this.client.request(
        { method: "prompts/list", params },
        ListPromptsResultSchema,
        this.getRequestOptions(metadata?.progressToken)
      )
    );
    return {
      prompts: response.prompts || [],
      nextCursor: response.nextCursor
    };
  }
  /**
   * Aggregate ALL pages of `prompts/list` via the SDK's high-level
   * cache-aware `client.listPrompts()`. See {@link listAllTools} for the
   * `cacheMode` semantics; this is the path the managed prompt list uses on
   * refresh.
   */
  async listAllPrompts(options) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const response = await this.invokeMcpClient(
      () => this.client.listPrompts(
        this.aggregateListParams(options?.metadata),
        this.getCacheableRequestOptions(options?.cacheMode)
      )
    );
    return { prompts: [...response.prompts] };
  }
  /**
   * Get a prompt by name
   * @param name Prompt name
   * @param args Optional prompt arguments
   * @param metadata Optional metadata to include in the request
   * @returns Prompt content
   */
  async getPrompt(name, args, metadata) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    const stringArgs = args ? convertPromptArguments(args) : {};
    const effectiveMeta = this.mergeMeta(metadata);
    const params = {
      name,
      arguments: stringArgs,
      ...effectiveMeta ? { _meta: effectiveMeta } : {}
    };
    const result = await this.invokeMcpClient(
      () => this.requestWithInputRequired(
        "prompts/get",
        params,
        GetPromptResultSchema,
        this.getRequestOptions(metadata?.progressToken)
      ),
      { method: "prompts/get", toolName: name }
    );
    const invocation = {
      result,
      timestamp: /* @__PURE__ */ new Date(),
      name,
      params: Object.keys(stringArgs).length > 0 ? stringArgs : void 0,
      metadata: effectiveMeta
    };
    this.dispatchTypedEvent("promptContentChange", {
      name,
      content: invocation,
      timestamp: invocation.timestamp
    });
    return invocation;
  }
  /**
   * Request completions for a resource template variable or prompt argument
   * @param ref Resource template reference or prompt reference
   * @param argumentName Name of the argument/variable to complete
   * @param argumentValue Current (partial) value of the argument
   * @param context Optional context with other argument values
   * @param metadata Optional metadata to include in the request
   * @returns Completion result with values array
   * @throws Error if client is not connected or request fails (except MethodNotFound)
   */
  async getCompletions(ref, argumentName, argumentValue, context, metadata) {
    if (!this.client) {
      return { values: [] };
    }
    try {
      const effectiveMeta = this.mergeMeta(metadata);
      const params = {
        ref,
        argument: {
          name: argumentName,
          value: argumentValue
        },
        ...context ? { context: { arguments: context } } : {},
        ...effectiveMeta ? { _meta: effectiveMeta } : {}
      };
      const response = await this.invokeMcpClient(
        () => this.client.complete(
          params,
          this.getRequestOptions(metadata?.progressToken)
        ),
        {
          method: "completion/complete",
          toolName: ref.type === "ref/prompt" ? ref.name : ref.uri
        }
      );
      return {
        values: response.completion.values || [],
        total: response.completion.total,
        hasMore: response.completion.hasMore
      };
    } catch (error) {
      if (error instanceof ProtocolError2 && error.code === ProtocolErrorCode2.MethodNotFound || error instanceof Error && (error.message.includes("Method not found") || error.message.includes("does not support completions"))) {
        return { values: [] };
      }
      throw new Error(
        `Failed to get completions: ${error instanceof Error ? error.message : String(error)}`,
        { cause: error }
      );
    }
  }
  /**
   * Fetch server info (capabilities, serverInfo, instructions) from cached initialize response
   * This does not send any additional MCP requests - it just reads cached data
   * Always called on connect
   */
  async fetchServerInfo() {
    if (!this.client) {
      return;
    }
    try {
      this.capabilities = this.client.getServerCapabilities();
      this.dispatchTypedEvent("capabilitiesChange", this.capabilities);
      this.serverInfo = this.client.getServerVersion();
      this.instructions = this.client.getInstructions();
      this.dispatchTypedEvent("serverInfoChange", this.serverInfo);
      if (this.instructions !== void 0) {
        this.dispatchTypedEvent("instructionsChange", this.instructions);
      }
      this.protocolEra = this.client.getProtocolEra();
      this.dispatchTypedEvent("protocolEraChange", this.protocolEra);
      this.discoverResult = this.client.getDiscoverResult();
      this.dispatchTypedEvent("discoverResultChange", this.discoverResult);
      this.protocolVersion = this.client.getNegotiatedProtocolVersion();
      this.dispatchTypedEvent("protocolVersionChange", this.protocolVersion);
    } catch {
    }
  }
  dispatchStderrLog(entry) {
    this.dispatchTypedEvent("stderrLog", entry);
  }
  dispatchFetchRequest(entry) {
    this.logger.info(
      {
        component: "InspectorClient",
        category: entry.category,
        fetchRequest: {
          url: entry.url,
          method: entry.method,
          headers: entry.requestHeaders,
          body: entry.requestBody ?? "[no body]"
        },
        fetchResponse: entry.error ? { error: entry.error } : {
          status: entry.responseStatus,
          statusText: entry.responseStatusText,
          headers: entry.responseHeaders,
          body: entry.responseBody
        }
      },
      `${entry.category} fetch`
    );
    this.dispatchTypedEvent("fetchRequest", entry);
  }
  dispatchFetchRequestBodyUpdate(id, responseBody) {
    this.dispatchTypedEvent("fetchRequestBodyUpdate", { id, responseBody });
  }
  /**
   * Get current session ID (from OAuth state authId)
   */
  getSessionId() {
    return this.sessionId;
  }
  /**
   * Set session ID (typically extracted from OAuth state)
   */
  setSessionId(sessionId) {
    this.sessionId = sessionId;
  }
  /**
   * Dispatch saveSession so FetchRequestLogState (or other listeners) can persist.
   * Call before OAuth redirect; listeners use sessionStorage with this sessionId.
   */
  saveSession() {
    if (!this.sessionId) return;
    this.dispatchTypedEvent("saveSession", { sessionId: this.sessionId });
  }
  /**
   * Get current roots
   */
  getRoots() {
    return this.roots !== void 0 ? [...this.roots] : [];
  }
  /**
   * Set roots and announce the change to the server.
   *
   * Note this does **not** enable the roots capability on a client that was
   * built without the constructor's `roots` option. `capabilities.roots` is
   * negotiated at `initialize` and the SDK refuses `registerCapabilities`
   * after connect, so such a client has no `roots/list` handler (see {@link
   * registerPeerRequestHandlers}) and would have to answer `-32601` if a
   * server asked. So on such a client the roots set here are stored and
   * readable via {@link getRoots}, but no server can ask for them and the
   * change is not announced — the SDK refuses `roots/list_changed` from a
   * client that never declared `roots.listChanged`, so the notification could
   * not have gone out anyway. Pass `roots` at construction — `[]` is enough —
   * in any client that may call this (#1797).
   *
   * The argument runs through `cleanRoots`, the same normalizer the
   * connect-time and settings-save paths use, so all three ways roots enter the
   * client agree and no caller can advertise a `Root` with no `uri` (the CLI's
   * `--roots-json` only checks that the JSON is an array). It is idempotent, so
   * a caller that already cleaned loses nothing.
   */
  async setRoots(roots) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    this.roots = cleanRoots(roots);
    this.dispatchTypedEvent("rootsChange", [...this.roots]);
    if (!this.rootsListChangedCapabilityAdvertised) {
      this.logger.warn(
        "setRoots() on a client that did not advertise `roots.listChanged`; roots are stored locally but the change is not announced"
      );
      return;
    }
    try {
      await this.client.notification({
        method: "notifications/roots/list_changed"
      });
    } catch (error) {
      this.logger.error(
        { error },
        "Failed to send roots/list_changed notification"
      );
    }
  }
  /**
   * Get list of currently subscribed resource URIs
   */
  getSubscribedResources() {
    return Array.from(this.subscribedResources);
  }
  /**
   * Check if a resource is currently subscribed
   */
  isSubscribedToResource(uri) {
    return this.subscribedResources.has(uri);
  }
  /**
   * Check if the server supports resource subscriptions
   */
  supportsResourceSubscriptions() {
    return this.capabilities?.resources?.subscribe === true;
  }
  /**
   * The negotiated protocol era once connected (SEP §7.8). Modern (2026-07-28)
   * connections manage resource subscriptions through a `subscriptions/listen`
   * stream instead of `resources/subscribe`; every other era is legacy.
   */
  isModernEra() {
    return this.protocolEra === "modern";
  }
  /**
   * Current state of the modern-era `subscriptions/listen` stream (#1630).
   * `active: false` on the legacy era (there is no persistent stream).
   */
  getResourceSubscriptionStreamState() {
    return this.modernStreamState;
  }
  setModernStreamState(next) {
    this.modernStreamState = next;
    this.dispatchTypedEvent("resourceSubscriptionStreamChange", next);
  }
  dispatchSubscriptionsChange() {
    this.dispatchTypedEvent(
      "resourceSubscriptionsChange",
      Array.from(this.subscribedResources)
    );
  }
  /**
   * The `subscriptions/listen` filter for the current modern subscriptions:
   * the subscribed URIs, plus the list-change opt-ins the Inspector already
   * tracks (config ∩ server capability) so the single stream also carries
   * list-change notifications — the spec models one listen stream for every
   * opted-in notification type (SEP §7.4).
   */
  buildSubscriptionFilter() {
    const filter = {};
    if (this.subscribedResources.size > 0) {
      filter.resourceSubscriptions = Array.from(this.subscribedResources);
    }
    if (this.listChangedNotifications.tools && this.capabilities?.tools?.listChanged) {
      filter.toolsListChanged = true;
    }
    if (this.listChangedNotifications.resources && this.capabilities?.resources?.listChanged) {
      filter.resourcesListChanged = true;
    }
    if (this.listChangedNotifications.prompts && this.capabilities?.prompts?.listChanged) {
      filter.promptsListChanged = true;
    }
    return filter;
  }
  /**
   * Whether the modern listen stream should be open: the built filter carries
   * something to listen for (#1920). Before #1920 this was "at least one URI is
   * subscribed", which made the stream unreachable on a server with no resources
   * — a tools-only server advertising `tools.listChanged` had no way to open it,
   * so `notifications/tools/list_changed` could never arrive. The filter already
   * modelled the list-change opt-ins; only the trigger was narrower than the
   * filter it built. This matches the SDK's own `ClientOptions.listChanged`
   * auto-open, which opens whenever the effective (config ∩ capability)
   * intersection is non-empty.
   *
   * Note this is deliberately *not* the same predicate as the stream state's
   * `active` — see `modernStreamActive()`.
   */
  wantsModernStream() {
    const filter = this.buildSubscriptionFilter();
    return (filter.resourceSubscriptions?.length ?? 0) > 0 || filter.toolsListChanged === true || filter.resourcesListChanged === true || filter.promptsListChanged === true;
  }
  /**
   * Whether the stream state reports `active` — i.e. whether the *Subscriptions*
   * UI has a stream to describe. That is a narrower question than
   * `wantsModernStream()`: `ResourceSubscriptionStreamState` drives the
   * Subscriptions section's badge, so a stream open purely for list-change
   * notifications (no subscribed URI) has nothing to report there and stays
   * `active: false` (#1920). Keeping the two apart also preserves the invariant
   * the rest of this file is written against — an empty subscribed set is never
   * announced alongside an `active` stream.
   */
  modernStreamActive() {
    return this.subscribedResources.size > 0;
  }
  /**
   * Open the modern listen stream at the end of a successful connect, when the
   * filter is non-empty (#1920). Only the list-change opt-ins can make it
   * non-empty here — the subscribed set is emptied by `resetSessionState()` on
   * the way in — so this is exactly the "server advertises a listChanged the
   * Inspector wants" case that had no trigger before.
   *
   * A failure is not allowed to fail the connect: the handshake succeeded and
   * every request-scoped feature works without this stream. It is reported the
   * way a lost stream is — hand it to the reconnect machinery, which retries
   * with backoff and settles on `"ended"` past the cap.
   *
   * Gated on the same generation test as `subscribeToResource` — see the long
   * comment there. The `connect` event has deliberately *not* been dispatched
   * yet (that is the point of running here), so a list-state consumer is not the
   * risk; what is, is anything else that bumps the generation while this
   * `listen()` is in flight. `statusChange` has already fired, and any
   * concurrent call on this instance qualifies: a `subscribeToResource` from a
   * caller restoring subscriptions, or a `disconnect()` — whose
   * `resetSubscriptionStream` bumps the generation too, making a reconcile here
   * arm a reconnect for a session that is already gone.
   */
  async openModernListenStreamOnConnect() {
    if (!this.isModernEra() || !this.wantsModernStream()) return;
    const generationBefore = this.modernListenGeneration;
    try {
      await this.refreshModernSubscription();
    } catch (error) {
      this.logger.error(
        { error },
        "Failed to open the modern subscriptions/listen stream on connect"
      );
      if (this.modernListenGeneration === generationBefore + 1) {
        this.reconcileModernStreamStateAfterFailedRefresh();
      }
    }
  }
  /** Cancel a pending reconnect re-listen, if any (#1630). */
  clearModernReconnectTimer() {
    if (this.modernReconnectTimer !== void 0) {
      clearTimeout(this.modernReconnectTimer);
      this.modernReconnectTimer = void 0;
    }
  }
  /**
   * (Re-)establish the modern `subscriptions/listen` stream to match the current
   * filter (#1630). Because the stream is not resumable, every filter change
   * re-lists: the existing stream is closed and a fresh `listen()` opened. With
   * an empty filter — no subscribed URIs *and* no enabled list-change opt-in the
   * server advertises — the stream is left closed (#1920).
   *
   * `modernListenGeneration` guards against races — if a newer refresh starts
   * while this one awaits its acknowledgement, the just-opened stream is
   * discarded rather than overwriting the newer one.
   */
  async refreshModernSubscription(fromReconnect = false) {
    if (!this.client) return;
    if (!fromReconnect) {
      this.clearModernReconnectTimer();
      this.modernReconnectAttempts = 0;
    }
    const generation = ++this.modernListenGeneration;
    const previous = this.modernSubscription;
    this.modernSubscription = null;
    if (previous) {
      await closeSubscriptionBestEffort(previous);
    }
    if (!this.wantsModernStream()) {
      this.setModernStreamState(INACTIVE_SUBSCRIPTION_STREAM_STATE);
      return;
    }
    const subscription = await this.client.listen(
      this.buildSubscriptionFilter(),
      this.getRequestOptions()
    );
    if (generation !== this.modernListenGeneration) {
      await closeSubscriptionBestEffort(subscription);
      return;
    }
    this.modernSubscription = subscription;
    this.modernReconnectAttempts = 0;
    this.setModernStreamState({
      active: this.modernStreamActive(),
      status: "acknowledged",
      honoredUris: subscription.honoredFilter.resourceSubscriptions ?? []
    });
    void subscription.closed.then(
      (reason) => this.onModernSubscriptionClosed(subscription, reason, generation),
      // The rejection arm exists because a `closed` that rejects carries no
      // reason to act on, and an unhandled rejection ends a Node process by
      // default. It is scoped to the rejection rather than chained after the
      // handler because the handler cannot throw today — it only assigns state,
      // dispatches on a native `EventTarget` (which reports listener throws
      // rather than propagating them) and arms a timer — and chaining a
      // `.catch` after it would silently abandon a re-listen if that ever
      // changed. (Closing a stream *resolves* `closed`; what the connect-path
      // close newly reaches is the handler running at all, where the reference
      // used to be dropped with `closed` pending forever.)
      () => {
      }
    );
  }
  /**
   * Handle termination of a modern listen stream (#1630). `"remote"` is an
   * unexpected drop — reconnect by re-listing (no resumability, so the re-listen
   * re-establishes the full filter). `"local"` (we closed it) and `"graceful"`
   * (server shutdown) are expected and leave the stream ended.
   */
  onModernSubscriptionClosed(subscription, reason, generation) {
    if (generation !== this.modernListenGeneration || this.modernSubscription !== subscription) {
      return;
    }
    this.modernSubscription = null;
    const shouldReconnect = reason === "remote" && !isTerminalStatus(this.status) && this.wantsModernStream();
    if (!shouldReconnect) {
      this.setModernStreamState({
        active: this.modernStreamActive(),
        status: "ended",
        honoredUris: []
      });
      return;
    }
    this.scheduleModernReconnect();
  }
  /**
   * Reconcile the stream state after a re-listen *this* call owned and lost
   * (#1797). Only correct for a caller that has not been superseded — a newer
   * refresh owns the state as well as the filter — so both call sites gate on
   * the generation first.
   *
   * The two branches are the empty and non-empty *filter* (#1920) — which is
   * "nothing subscribed" only when no list-change opt-in is live. The empty case
   * is the ordinary one: nothing to listen for, no stream, inactive. The
   * non-empty one exists because a failed re-listen leaves `modernSubscription`
   * null with the filter still wanting a stream, and nothing else will notice: the reconnect machinery is reachable only from a stream that closed
   * or a reconnect that failed, and neither happened here. Left alone, the state
   * keeps whatever the last success (or the optimistic `"connecting"`) wrote — a
   * badge that will never change over subscriptions the server may never have
   * honored, recoverable only by an Unsubscribe/Subscribe toggle (a fresh
   * Subscribe early-returns on the URI already being in the set).
   *
   * So it reconnects rather than settling for an honest-but-dead `"ended"`:
   * every other route to "stream gone, filter live" either expects the close or
   * has exhausted the retry cap, and this is the one that has made no attempt
   * at all. `scheduleModernReconnect` fits as-is — a user-initiated refresh
   * already reset `modernReconnectAttempts`, so it starts at the base delay; the
   * timer bails on a terminal status or an emptied filter; and past the cap
   * `onModernReconnectFailed` lands on the same `"ended"` badge. The state
   * therefore becomes true or ends after a real attempt. The caller still sees
   * its error either way — the retry is about the subscriptions, not the call.
   */
  reconcileModernStreamStateAfterFailedRefresh() {
    if (!this.wantsModernStream()) {
      this.setModernStreamState(INACTIVE_SUBSCRIPTION_STREAM_STATE);
      return;
    }
    this.scheduleModernReconnect();
  }
  /**
   * Schedule a reconnect re-listen after the current backoff delay (#1630).
   * `modernReconnectAttempts` reflects the number of *consecutive failed*
   * re-lists (reset to 0 on any successful acknowledgement), so the delay grows
   * only while re-listing keeps failing.
   */
  scheduleModernReconnect() {
    this.setModernStreamState({
      active: this.modernStreamActive(),
      status: "reconnecting",
      honoredUris: []
    });
    const delay = Math.min(
      MODERN_RECONNECT_BASE_MS * 2 ** this.modernReconnectAttempts,
      MODERN_RECONNECT_MAX_MS
    );
    this.clearModernReconnectTimer();
    this.modernReconnectTimer = setTimeout(() => {
      this.modernReconnectTimer = void 0;
      if (isTerminalStatus(this.status) || !this.wantsModernStream()) {
        return;
      }
      this.refreshModernSubscription(true).catch(
        () => this.onModernReconnectFailed()
      );
    }, delay);
  }
  /**
   * A reconnect re-listen failed (#1630). Count it and either retry with a
   * longer backoff or, past the consecutive-failure cap, give up and mark the
   * stream ended (re-subscribing resets the run and tries again).
   */
  onModernReconnectFailed() {
    this.modernReconnectAttempts += 1;
    if (this.modernReconnectAttempts > MODERN_RECONNECT_MAX_ATTEMPTS || isTerminalStatus(this.status) || !this.wantsModernStream()) {
      this.setModernStreamState({
        active: this.modernStreamActive(),
        status: "ended",
        honoredUris: []
      });
      return;
    }
    this.scheduleModernReconnect();
  }
  /**
   * Subscribe to a resource to receive update notifications.
   *
   * Legacy era: sends `resources/subscribe`. Modern era (2026-07-28): adds the
   * URI to the `subscriptions/listen` filter and re-lists (#1630). In both eras
   * `notifications/resources/updated` is delivered through the same handler.
   *
   * @param uri - The URI of the resource to subscribe to
   * @throws Error if client is not connected or server doesn't support subscriptions
   */
  async subscribeToResource(uri) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    if (!this.supportsResourceSubscriptions()) {
      throw new Error("Server does not support resource subscriptions");
    }
    try {
      if (this.isModernEra()) {
        if (this.subscribedResources.has(uri)) return;
        this.subscribedResources.add(uri);
        this.dispatchSubscriptionsChange();
        this.setModernStreamState({
          active: true,
          status: "connecting",
          honoredUris: this.modernStreamState.honoredUris
        });
        const generationBefore = this.modernListenGeneration;
        try {
          await this.refreshModernSubscription();
        } catch (error) {
          if (this.modernListenGeneration === generationBefore + 1) {
            this.subscribedResources.delete(uri);
            this.reconcileModernStreamStateAfterFailedRefresh();
            this.dispatchSubscriptionsChange();
          }
          throw error;
        }
        return;
      }
      await this.client.subscribeResource({ uri }, this.getRequestOptions());
      this.subscribedResources.add(uri);
      this.dispatchSubscriptionsChange();
    } catch (error) {
      throw new Error(
        `Failed to subscribe to resource: ${error instanceof Error ? error.message : String(error)}`,
        { cause: error }
      );
    }
  }
  /**
   * Unsubscribe from a resource.
   *
   * Legacy era: sends `resources/unsubscribe`. Modern era: drops the URI from
   * the `subscriptions/listen` filter and re-lists (closing the stream once the
   * last URI is removed) (#1630).
   *
   * @param uri - The URI of the resource to unsubscribe from
   * @throws Error if client is not connected
   */
  async unsubscribeFromResource(uri) {
    if (!this.client) {
      throw new Error("Client is not connected");
    }
    try {
      if (this.isModernEra()) {
        if (!this.subscribedResources.delete(uri)) return;
        if (this.subscribedResources.size === 0) {
          this.setModernStreamState(INACTIVE_SUBSCRIPTION_STREAM_STATE);
        }
        this.dispatchSubscriptionsChange();
        const generationBefore = this.modernListenGeneration;
        try {
          await this.refreshModernSubscription();
        } catch (error) {
          if (this.modernListenGeneration === generationBefore + 1) {
            this.reconcileModernStreamStateAfterFailedRefresh();
          }
          throw error;
        }
      } else {
        await this.client.unsubscribeResource(
          { uri },
          this.getRequestOptions()
        );
        this.subscribedResources.delete(uri);
        this.dispatchSubscriptionsChange();
      }
    } catch (error) {
      throw new Error(
        `Failed to unsubscribe from resource: ${error instanceof Error ? error.message : String(error)}`,
        { cause: error }
      );
    }
  }
  // ============================================================================
  // OAuth Support (delegated to oauthManager)
  // ============================================================================
  ensureOAuthManager() {
    if (!this.oauthManager) {
      throw new Error("OAuth not configured. Call setOAuthConfig() first.");
    }
    return this.oauthManager;
  }
  /**
   * Get server URL from transport config (full URL including path, for OAuth discovery)
   */
  getServerUrl() {
    if (this.transportConfig.type === "sse" || this.transportConfig.type === "streamable-http") {
      return this.transportConfig.url;
    }
    throw new Error(
      "OAuth is only supported for HTTP-based transports (SSE, streamable-http)"
    );
  }
  /**
   * Set OAuth configuration
   */
  setOAuthConfig(config) {
    if (!this.oauthManager) {
      throw new Error(
        "OAuth config must be set at creation. Pass oauth in constructor."
      );
    }
    this.oauthManager.setOAuthConfig(config);
  }
  /**
   * Initiates OAuth flow. Can be called directly by user or automatically
   * triggered by 401 errors.
   */
  async authenticate() {
    return this.ensureOAuthManager().authenticate();
  }
  /**
   * Satisfy a mid-session auth challenge (token refresh, step-up, or interactive re-auth).
   */
  async handleAuthChallenge(challenge, options) {
    return this.ensureOAuthManager().handleAuthChallenge(challenge, options);
  }
  /**
   * Re-read OAuth storage and test whether a challenge is already satisfied.
   * See {@link OAuthManager.checkAuthChallengeSatisfied}.
   */
  async checkAuthChallengeSatisfied(challenge) {
    return this.ensureOAuthManager().checkAuthChallengeSatisfied(challenge);
  }
  /**
   * Push recovered OAuth auth state to the remote backend (same MCP session).
   */
  async pushRemoteAuthState() {
    if (!(this.baseTransport instanceof RemoteClientTransport)) {
      return;
    }
    await this.baseTransport.pushAuthState();
  }
  /**
   * Handle an ambient (SSE) auth challenge when no command-scoped send is active.
   * Recovers session tokens on the remote backend; does not retry RPCs.
   */
  async handleAmbientAuthChallenge(challenge) {
    const key = this.ambientAuthChallengeKey(challenge);
    const existing = this.ambientAuthChallengeInFlight.get(key);
    if (existing) {
      return existing;
    }
    const promise = this.runAmbientAuthChallenge(challenge);
    this.ambientAuthChallengeInFlight.set(key, promise);
    try {
      await promise;
    } finally {
      if (this.ambientAuthChallengeInFlight.get(key) === promise) {
        this.ambientAuthChallengeInFlight.delete(key);
      }
    }
  }
  async runAmbientAuthChallenge(challenge) {
    try {
      this.dispatchTypedEvent("authChallengeAmbient", { challenge });
      const oauthManager = this.oauthManager;
      if (!oauthManager) {
        return;
      }
      const outcome = await oauthManager.handleAuthChallenge(challenge);
      if (outcome.kind === "satisfied") {
        if (this.baseTransport instanceof RemoteClientTransport) {
          await this.pushRemoteAuthState();
        } else {
          await this.reconnectAfterAuthRecovery();
        }
        this.dispatchTypedEvent("authChallengeRecovered", { challenge });
      } else if (outcome.kind === "step_up_confirm") {
        this.dispatchTypedEvent("authChallengeInteractive", {
          challenge: outcome.challenge,
          authorizationUrl: EMA_STEP_UP_PENDING_URL
        });
      } else if (outcome.kind === "interactive") {
        this.dispatchTypedEvent("authChallengeInteractive", {
          challenge: outcome.challenge,
          authorizationUrl: outcome.authorizationUrl
        });
      } else {
        this.dispatchTypedEvent("oauthError", { error: outcome.error });
      }
    } catch (error) {
      this.dispatchTypedEvent("oauthError", {
        error: error instanceof Error ? error : new Error(String(error))
      });
    }
  }
  ambientAuthChallengeKey(challenge) {
    const requiredScopes = [...challenge.requiredScopes ?? []].sort().join(" ");
    const authorizationScopes = [...challenge.authorizationScopes ?? []].sort().join(" ");
    return `${challenge.reason}:${requiredScopes}:${authorizationScopes}`;
  }
  /**
   * Full disconnect + reconnect after ambient auth recovery on direct transports.
   */
  async reconnectAfterAuthRecovery() {
    await this.disconnect().catch(() => {
    });
    await this.dropCachedTransport();
    await this.connect();
  }
  /** Direct (non-remote) OAuth transports recover via fetch intercept + handleAuthChallenge. */
  usesDirectAuthRecovery() {
    return this.directAuthRecovery && this.directAuthRecoveryActive === true;
  }
  /**
   * True when connect() sends the SDK's `server/discover` negotiation probe —
   * i.e. `protocolEra` is "auto", or "modern" (`{ pin: MODERN_PROTOCOL_VERSION }`).
   * Legacy is the default for an absent `mode`, matching the SDK.
   *
   * `versionNegotiation` is a public option, so a caller can pin a revision the
   * repo's own {@link eraToVersionNegotiation} never produces (it only ever
   * pins {@link MODERN_PROTOCOL_VERSION}). That is still "probing" and
   * deliberately reports true: per the SDK, *any* `{ pin }` sends the
   * connect-time `server/discover` — "the connect-time `server/discover` must
   * offer it. No fallback" — so the pinned revision doesn't change whether the
   * probe (and hence the buried-401 problem) happens. Only "legacy" skips it.
   */
  probesProtocolEra() {
    const mode = this.versionNegotiation.mode;
    return mode !== void 0 && mode !== "legacy";
  }
  async withDirectAuthRecovery(operation, context, attempt = 0) {
    try {
      return await operation(attempt);
    } catch (err) {
      if (attempt >= 1 || !this.usesDirectAuthRecovery()) {
        throw err;
      }
      if (!isAuthChallengeError(err)) {
        throw err;
      }
      if (this.authRecoveryDepth >= _InspectorClient.MAX_NESTED_AUTH_RECOVERIES) {
        throw err;
      }
      const challenge = parseAuthChallengeFromError(err, context);
      if (!challenge) {
        throw err;
      }
      if (context?.method || context?.toolName) {
        this.dispatchTypedEvent("authChallengeCommand", { challenge });
      } else {
        this.dispatchTypedEvent("authChallengeAmbient", { challenge });
      }
      const outcome = await this.handleAuthChallenge(challenge);
      if (outcome.kind === "satisfied") {
        if (this.activeToolCallAbortController) {
          this.activeToolCallAbortController = void 0;
        }
        this.authRecoveryDepth += 1;
        try {
          await this.reconnectAfterAuthRecovery();
        } finally {
          this.authRecoveryDepth -= 1;
        }
        this.dispatchTypedEvent("authChallengeRecovered", { challenge });
        return this.withDirectAuthRecovery(operation, context, attempt + 1);
      }
      if (outcome.kind === "step_up_confirm") {
        throw new AuthRecoveryRequiredError(
          EMA_STEP_UP_PENDING_URL,
          outcome.challenge,
          { emaStepUpConfirm: true }
        );
      }
      if (outcome.kind === "interactive") {
        throw new AuthRecoveryRequiredError(
          outcome.authorizationUrl,
          outcome.challenge
        );
      }
      this.dispatchTypedEvent("oauthError", { error: outcome.error });
      throw outcome.error;
    }
  }
  async invokeMcpClient(operation, context) {
    if (!this.usesDirectAuthRecovery()) {
      return operation(0);
    }
    return this.withDirectAuthRecovery(operation, context);
  }
  /**
   * Completes OAuth flow with authorization code from the redirect callback.
   * Direct transports reconnect after token exchange so the live MCP session
   * picks up the new Bearer token (mirrors silent recovery reconnect).
   */
  async completeOAuthFlow(authorizationCode, iss) {
    await this.ensureOAuthManager().completeOAuthFlow(authorizationCode, iss);
    if (this.usesDirectAuthRecovery()) {
      await this.reconnectAfterAuthRecovery();
    }
  }
  /**
   * Navigate to the authorization server for interactive recovery.
   */
  async beginInteractiveAuthorization(authorizationUrl) {
    return this.ensureOAuthManager().beginInteractiveAuthorization(
      authorizationUrl
    );
  }
  /** Remote Hono session id when using {@link RemoteClientTransport}. */
  getRemoteBackendSessionId() {
    if (this.baseTransport instanceof RemoteClientTransport) {
      return this.baseTransport.getRemoteBackendSessionId();
    }
    return void 0;
  }
  /**
   * Finish OAuth after a full-page redirect and reconnect (or reattach) the MCP session.
   */
  async resumeAfterOAuth(authorizationCode, options) {
    await this.completeOAuthFlow(authorizationCode, options?.iss);
    const remoteSessionId = options?.remoteSessionId;
    const transport = this.baseTransport;
    if (remoteSessionId && transport instanceof RemoteClientTransport) {
      try {
        await transport.attachToSession(remoteSessionId);
        await transport.pushAuthState();
        if (this.status !== "connected") {
          await this.connect();
        }
        return;
      } catch {
      }
    }
    if (this.status !== "connected") {
      await this.connect();
    }
  }
  /**
   * Gets current OAuth tokens (if authorized)
   */
  async getOAuthTokens() {
    if (!this.oauthManager) {
      return void 0;
    }
    return this.oauthManager.getOAuthTokens();
  }
  /**
   * Clears OAuth tokens and client information
   */
  async clearOAuthTokens() {
    await this.oauthManager?.clearOAuthTokens();
  }
  /**
   * Checks if client is currently OAuth authorized
   */
  async isOAuthAuthorized() {
    if (!this.oauthManager) {
      return false;
    }
    return this.oauthManager.isOAuthAuthorized();
  }
  /**
   * In-memory OAuth flow snapshot. Undefined when no flow has run on this
   * client instance; use {@link getOAuthState} for persisted authorization state.
   */
  getOAuthFlowState() {
    return this.oauthManager?.getOAuthFlowState();
  }
  /** Current step when an OAuth flow is active. */
  getOAuthFlowStep() {
    return this.oauthManager?.getOAuthFlowStep();
  }
  /**
   * Persisted OAuth authorization snapshot for this HTTP server (storage +
   * config). Undefined for stdio transports or when OAuth is not configured.
   */
  async getOAuthState() {
    if (!this.isHttpOAuthConfig() || !this.oauthManager) {
      return void 0;
    }
    return this.oauthManager.getOAuthState();
  }
};

// ../../core/mcp/state/managedListState.ts
import { SdkError, SdkErrorCode } from "@modelcontextprotocol/client";
var DEFAULT_LIST_CHANGED_DEBOUNCE_MS = 250;
function isClientDecodeRejection(err) {
  return SdkError.isInstance(err) && (err.code === SdkErrorCode.InvalidResult || err.code === SdkErrorCode.UnsupportedResultType);
}
var ManagedListState = class extends TypedEventTarget {
  items = [];
  client = null;
  unsubscribe = null;
  _metadata = void 0;
  listChanged = false;
  // The last fetch's failure, kept as observable state so a load that fails
  // (a transport error, or a result the SDK codec rejects as invalid) is
  // rendered by the list panel instead of vanishing into an unhandled
  // rejection — which read as "this server has no tools" (#1953).
  error = null;
  config;
  // Debounce a burst of `list_changed` notifications into a single
  // refresh (or one indicator light) once it settles.
  listChangedTimer = null;
  // Second line of defense beyond the debounce, for the auto-refresh path:
  // while a refresh is fetching, a new (post-debounce) notification queues a
  // single re-run instead
  // of firing another concurrent paginated fetch.
  running = false;
  runQueued = false;
  constructor(client, config) {
    super();
    this.client = client;
    this.config = config;
    const onConnect = () => {
      if (this.config.deferWhenPaginated && this.client?.getServerSettings()?.paginatedLists) {
        return;
      }
      void this.refresh().catch(() => {
      });
    };
    const onListChanged = () => {
      if (this.listChangedTimer !== null) clearTimeout(this.listChangedTimer);
      this.listChangedTimer = setTimeout(() => {
        this.listChangedTimer = null;
        this.runListChanged();
      }, config.debounceMs);
    };
    const onStatusChange = () => {
      if (isTerminalStatus(this.client?.getStatus())) {
        if (this.listChangedTimer !== null) {
          clearTimeout(this.listChangedTimer);
          this.listChangedTimer = null;
        }
        this.items = [];
        this.dispatchChange();
        this.setListChanged(false);
        this.setError(null);
      }
    };
    this.client.addEventListener("connect", onConnect);
    this.client.addEventListener(config.listChangedEvent, onListChanged);
    this.client.addEventListener("statusChange", onStatusChange);
    this.unsubscribe = () => {
      if (this.listChangedTimer !== null) {
        clearTimeout(this.listChangedTimer);
        this.listChangedTimer = null;
      }
      if (this.client) {
        this.client.removeEventListener("connect", onConnect);
        this.client.removeEventListener(config.listChangedEvent, onListChanged);
        this.client.removeEventListener("statusChange", onStatusChange);
      }
      this.client = null;
    };
  }
  /**
   * The debounced list-changed action. With auto-refresh on, pull the new list
   * (guarded against overlap so a slow older fetch can't clobber a newer list
   * via last-write-wins `applyItems`). With auto-refresh off, lights the
   * indicator without any network — the user pulls the new list via Refresh
   * (#1402, #1444). Lists without an indicator (resource templates) do nothing
   * in the off case.
   */
  runListChanged() {
    if (this.running) {
      this.runQueued = true;
      return;
    }
    void this.runListChangedOnce();
  }
  async runListChangedOnce() {
    this.running = true;
    try {
      do {
        this.runQueued = false;
        const settings = this.client?.getServerSettings();
        const skipAggregate = this.config.deferWhenPaginated && settings?.paginatedLists;
        if (!skipAggregate && settings?.autoRefreshOnListChanged) {
          await this.refresh(void 0, "refresh").catch(() => {
          });
        } else if (this.config.supportsIndicator) {
          this.setListChanged(true);
        }
      } while (this.runQueued);
    } finally {
      this.running = false;
    }
  }
  /** Defensive copy of the current list. */
  getItems() {
    return [...this.items];
  }
  /** Whether a `list_changed` arrived since the last refresh (indicator on). */
  getListChanged() {
    return this.listChanged;
  }
  /**
   * Clear the list-changed flag — called when the user refreshes the list. The
   * blind light on the notification leaves the indicator set until the user
   * acknowledges by pulling.
   */
  clearListChanged() {
    this.setListChanged(false);
  }
  /**
   * Dispatch a configured event by name. `dispatchTypedEvent`'s
   * `EventMap[K] extends void ? [] : [detail]` overload can't resolve when the
   * key is a generic `keyof M`, so we narrow the method signature here. The
   * concrete subclass event maps keep the call sites type-safe.
   */
  emit(type, detail) {
    this.dispatchTypedEvent(
      type,
      detail
    );
  }
  setListChanged(value) {
    if (this.listChanged === value) return;
    this.listChanged = value;
    this.emit("listChangedChange", value);
  }
  /** The last fetch's failure, or `null` when the last fetch succeeded. */
  getError() {
    return this.error;
  }
  // Compared by identity rather than message: two distinct failures with the
  // same text are still two events, and a re-render on a repeat failure is
  // cheap next to silently coalescing them.
  setError(value) {
    if (this.error === value) return;
    this.error = value;
    this.emit("errorChange", value);
  }
  setMetadata(metadata) {
    this._metadata = metadata;
  }
  /**
   * Refresh the full list. `cacheMode` selects the SDK cache disposition for
   * this fetch: `undefined` (the connect-time load) uses the default `'use'`;
   * a user-initiated or auto refresh passes `'refresh'` to force a
   * cache-bypassing round trip and re-store the fresh aggregate.
   *
   * A failure is recorded as observable state (`getError`) AND re-thrown: the
   * state drives the panel's error rendering, while the rejection is what the
   * caller's auth-recovery wrapper keys off to detect a 401 and start a
   * re-authorization. Callers with nobody to await them (the connect-time load,
   * the `list_changed` auto-refresh) catch it explicitly (#1953).
   */
  async refresh(metadata, cacheMode) {
    let next;
    try {
      next = await this.fetchItems(metadata, cacheMode);
    } catch (err) {
      const error = err instanceof Error ? err : new Error(String(err));
      this.setError(error);
      if (isClientDecodeRejection(err)) {
        this.client?.markResponseRejected?.(
          this.config.listMethod,
          error.message
        );
      }
      throw err;
    }
    this.setError(null);
    if (next === null) return this.getItems();
    this.applyItems(next);
    return this.getItems();
  }
  /**
   * Fetch the complete list (all pages, via the SDK's cache-aware verb), then
   * `applyItems` commits it (see `refresh`). Returns `null` when not connected,
   * or `[]` when the server doesn't advertise the gating capability (calling
   * the list method there returns -32601 "Method not found", which would spam
   * the console; empty list is the right semantics).
   */
  async fetchItems(metadata, cacheMode) {
    const client = this.client;
    if (!client || client.getStatus() !== "connected") return null;
    if (!client.getCapabilities()?.[this.config.capabilityKey]) return [];
    const effectiveMetadata = metadata ?? this._metadata;
    return this.config.fetchAll(client, cacheMode, effectiveMetadata);
  }
  /** Commit a fetched list as the current one and notify subscribers. */
  applyItems(items) {
    this.items = items;
    this.dispatchChange();
  }
  dispatchChange() {
    this.emit(this.config.changeEvent, this.getItems());
  }
  /** Unsubscribe from the client and drop the list; idempotent. */
  destroy() {
    this.unsubscribe?.();
    this.unsubscribe = null;
    this.items = [];
  }
};

// ../../core/mcp/state/managedToolsState.ts
var ManagedToolsState = class extends ManagedListState {
  constructor(client, debounceMs = DEFAULT_LIST_CHANGED_DEBOUNCE_MS) {
    super(client, {
      listMethod: "tools/list",
      changeEvent: "toolsChange",
      listChangedEvent: "toolsListChanged",
      capabilityKey: "tools",
      deferWhenPaginated: true,
      supportsIndicator: true,
      debounceMs,
      fetchAll: async (c, cacheMode, metadata) => {
        const result = await c.listAllTools({ cacheMode, metadata });
        return result.tools;
      }
    });
  }
  getTools() {
    return this.getItems();
  }
};

// ../../core/mcp/state/messageLogState.ts
var MessageLogState = class extends TypedEventTarget {
  messages = [];
  /** Pending request entries by JSON-RPC message id for matching responses. */
  pendingRequestEntries = /* @__PURE__ */ new Map();
  client = null;
  unsubscribe = null;
  maxMessages;
  constructor(client, options = {}) {
    super();
    this.maxMessages = options.maxMessages ?? 1e3;
    this.client = client;
    const pushEntry = (entry) => {
      if (this.maxMessages > 0 && this.messages.length >= this.maxMessages) {
        this.messages.shift();
      }
      this.messages.push(entry);
      this.dispatchTypedEvent("message", entry);
      this.dispatchTypedEvent("messagesChange", this.getMessages());
    };
    const onMessage = (event) => {
      const entry = event.detail;
      if (entry.direction === "request") {
        const reqId = "id" in entry.message ? entry.message.id : void 0;
        if (reqId !== void 0) {
          this.pendingRequestEntries.set(reqId, entry);
        }
        pushEntry(entry);
        return;
      }
      if (entry.direction === "response") {
        const messageId = "id" in entry.message ? entry.message.id : void 0;
        const requestEntry = messageId !== void 0 ? this.pendingRequestEntries.get(messageId) : void 0;
        if (requestEntry) {
          this.pendingRequestEntries.delete(messageId);
          requestEntry.response = entry.message;
          requestEntry.duration = entry.timestamp.getTime() - requestEntry.timestamp.getTime();
          this.dispatchTypedEvent("message", requestEntry);
          this.dispatchTypedEvent("messagesChange", this.getMessages());
          return;
        }
      }
      pushEntry(entry);
    };
    const onResponseRejected = (event) => {
      const { id, reason } = event.detail;
      for (let i = this.messages.length - 1; i >= 0; i--) {
        const entry = this.messages[i];
        const entryId = entry.message.id;
        if (entryId !== id) continue;
        if (entry.direction === "notification") continue;
        if (entry.direction === "request" && !entry.response) continue;
        entry.clientError = reason;
        this.dispatchTypedEvent("message", entry);
        this.dispatchTypedEvent("messagesChange", this.getMessages());
        return;
      }
    };
    const onStatusChange = () => {
      if (isTerminalStatus(this.client?.getStatus())) {
        this.messages = [];
        this.pendingRequestEntries.clear();
        this.dispatchTypedEvent("messagesChange", []);
      }
    };
    this.client.addEventListener("message", onMessage);
    this.client.addEventListener("responseRejected", onResponseRejected);
    this.client.addEventListener("statusChange", onStatusChange);
    this.unsubscribe = () => {
      if (this.client) {
        this.client.removeEventListener("message", onMessage);
        this.client.removeEventListener("responseRejected", onResponseRejected);
        this.client.removeEventListener("statusChange", onStatusChange);
      }
      this.client = null;
    };
  }
  getMessages(predicate) {
    if (predicate) {
      return this.messages.filter(predicate);
    }
    return [...this.messages];
  }
  /**
   * Remove messages from history. When `predicate` is provided, removes only
   * entries for which predicate returns true. When omitted, clears all messages.
   * Dispatches messagesChange only if the list actually changed.
   */
  clearMessages(predicate) {
    const before = this.messages.length;
    this.messages = predicate ? this.messages.filter((m) => !predicate(m)) : [];
    if (predicate) {
      const survivors = new Set(this.messages);
      for (const [id, entry] of this.pendingRequestEntries) {
        if (!survivors.has(entry)) {
          this.pendingRequestEntries.delete(id);
        }
      }
    } else {
      this.pendingRequestEntries.clear();
    }
    if (this.messages.length !== before) {
      this.dispatchTypedEvent("messagesChange", this.getMessages());
    }
  }
  destroy() {
    this.unsubscribe?.();
    this.unsubscribe = null;
    this.messages = [];
    this.pendingRequestEntries.clear();
  }
};

// ../../core/mcp/state/fetchRequestLogState.ts
var FetchRequestLogState = class extends TypedEventTarget {
  fetchRequests = [];
  client = null;
  unsubscribe = null;
  maxFetchRequests;
  logger;
  constructor(client, options = {}) {
    super();
    this.maxFetchRequests = options.maxFetchRequests ?? 1e3;
    this.logger = options.logger ?? silentLogger;
    this.client = client;
    const onFetchRequest = (event) => {
      const entry = event.detail;
      if (this.maxFetchRequests > 0 && this.fetchRequests.length >= this.maxFetchRequests) {
        this.fetchRequests.shift();
      }
      this.fetchRequests.push(entry);
      this.dispatchTypedEvent("fetchRequest", entry);
      this.dispatchTypedEvent("fetchRequestsChange", this.getFetchRequests());
    };
    this.client.addEventListener("fetchRequest", onFetchRequest);
    const onFetchRequestBodyUpdate = (event) => {
      const { id, responseBody } = event.detail;
      const idx = this.fetchRequests.findIndex((e) => e.id === id);
      if (idx === -1) {
        const rotatedOut = this.maxFetchRequests > 0 && this.fetchRequests.length >= this.maxFetchRequests;
        if (rotatedOut) {
          this.logger.warn(
            { fetchRequestId: id, maxFetchRequests: this.maxFetchRequests },
            "fetchRequestBodyUpdate dropped: entry rotated out before body arrived"
          );
          this.dispatchTypedEvent("fetchRequestBodyDropped", {
            id,
            maxFetchRequests: this.maxFetchRequests
          });
        }
        return;
      }
      this.fetchRequests[idx] = {
        ...this.fetchRequests[idx],
        responseBody
      };
      this.dispatchTypedEvent("fetchRequestsChange", this.getFetchRequests());
    };
    this.client.addEventListener(
      "fetchRequestBodyUpdate",
      onFetchRequestBodyUpdate
    );
    const sessionStorage2 = options.sessionStorage;
    const sessionId = options.sessionId;
    if (sessionStorage2) {
      const onSaveSession = (event) => {
        const { sessionId: id } = event.detail;
        const state = {
          fetchRequests: this.getFetchRequests(),
          createdAt: Date.now(),
          updatedAt: Date.now()
        };
        sessionStorage2.saveSession(id, state).catch(() => {
        });
      };
      this.client.addEventListener("saveSession", onSaveSession);
      this.unsubscribe = () => {
        if (this.client) {
          this.client.removeEventListener("fetchRequest", onFetchRequest);
          this.client.removeEventListener(
            "fetchRequestBodyUpdate",
            onFetchRequestBodyUpdate
          );
          this.client.removeEventListener("saveSession", onSaveSession);
        }
        this.client = null;
      };
      if (sessionId) {
        sessionStorage2.loadSession(sessionId).then((state) => {
          if (this.client && state?.fetchRequests?.length) {
            this.hydrateFetchRequests(state.fetchRequests);
          }
        }).catch(() => {
        });
      }
    } else {
      this.unsubscribe = () => {
        if (this.client) {
          this.client.removeEventListener("fetchRequest", onFetchRequest);
          this.client.removeEventListener(
            "fetchRequestBodyUpdate",
            onFetchRequestBodyUpdate
          );
        }
        this.client = null;
      };
    }
  }
  // Restore persisted entries (e.g. the pre-redirect OAuth Network log loaded
  // on the `/oauth/callback` page). Merges rather than replaces: the async
  // load races against entries appended live by the resuming connect
  // (`completeOAuthFlow` + transport handshake), so we must not clobber
  // whichever arrived first. Restored entries are older, so they go in front;
  // duplicates (by id) already present from a live append are skipped.
  hydrateFetchRequests(entries) {
    if (entries.length === 0) return;
    const existingIds = new Set(this.fetchRequests.map((e) => e.id));
    const restored = entries.filter((e) => !existingIds.has(e.id));
    if (restored.length === 0) return;
    const merged = [...restored, ...this.fetchRequests];
    this.fetchRequests = this.maxFetchRequests > 0 ? merged.slice(-this.maxFetchRequests) : merged;
    this.dispatchTypedEvent("fetchRequestsChange", this.getFetchRequests());
  }
  getFetchRequests() {
    return [...this.fetchRequests];
  }
  /**
   * Adjust the retention cap live (e.g. when the user edits the per-server
   * `maxFetchRequests` setting) without reconnecting. Shrinking the cap trims
   * the oldest entries immediately and re-emits so the Network UI updates;
   * growing it (or setting 0 = unlimited) just takes effect for future appends.
   */
  setMaxFetchRequests(max) {
    if (max === this.maxFetchRequests) return;
    this.maxFetchRequests = max;
    if (max > 0 && this.fetchRequests.length > max) {
      this.fetchRequests = this.fetchRequests.slice(-max);
      this.dispatchTypedEvent("fetchRequestsChange", this.getFetchRequests());
    }
  }
  /**
   * Clear all fetch requests. Dispatches fetchRequestsChange only if the list
   * was non-empty.
   */
  clearFetchRequests() {
    if (this.fetchRequests.length === 0) return;
    this.fetchRequests = [];
    this.dispatchTypedEvent("fetchRequestsChange", []);
  }
  destroy() {
    this.unsubscribe?.();
    this.unsubscribe = null;
    this.fetchRequests = [];
  }
};

// ../../core/mcp/state/stderrLogState.ts
var StderrLogState = class extends TypedEventTarget {
  stderrLogs = [];
  client = null;
  unsubscribe = null;
  maxStderrLogEvents;
  constructor(client, options = {}) {
    super();
    this.maxStderrLogEvents = options.maxStderrLogEvents ?? 1e3;
    this.client = client;
    const onStderrLog = (event) => {
      const entry = event.detail;
      if (this.maxStderrLogEvents > 0 && this.stderrLogs.length >= this.maxStderrLogEvents) {
        this.stderrLogs.shift();
      }
      this.stderrLogs.push(entry);
      this.dispatchTypedEvent("stderrLog", entry);
      this.dispatchTypedEvent("stderrLogsChange", this.getStderrLogs());
    };
    this.client.addEventListener("stderrLog", onStderrLog);
    this.unsubscribe = () => {
      if (this.client) {
        this.client.removeEventListener("stderrLog", onStderrLog);
      }
      this.client = null;
    };
  }
  getStderrLogs() {
    return [...this.stderrLogs];
  }
  /**
   * Clear all stderr log entries. Dispatches stderrLogsChange only if the
   * list was non-empty.
   */
  clearStderrLogs() {
    if (this.stderrLogs.length === 0) return;
    this.stderrLogs = [];
    this.dispatchTypedEvent("stderrLogsChange", []);
  }
  destroy() {
    this.unsubscribe?.();
    this.unsubscribe = null;
    this.stderrLogs = [];
  }
};

// ../../core/mcp/state/managedResourcesState.ts
var ManagedResourcesState = class extends ManagedListState {
  constructor(client, debounceMs = DEFAULT_LIST_CHANGED_DEBOUNCE_MS) {
    super(client, {
      listMethod: "resources/list",
      changeEvent: "resourcesChange",
      listChangedEvent: "resourcesListChanged",
      capabilityKey: "resources",
      deferWhenPaginated: true,
      supportsIndicator: true,
      debounceMs,
      fetchAll: async (c, cacheMode, metadata) => {
        const result = await c.listAllResources({ cacheMode, metadata });
        return result.resources;
      }
    });
  }
  getResources() {
    return this.getItems();
  }
};

// ../../core/mcp/state/managedResourceTemplatesState.ts
var ManagedResourceTemplatesState = class extends ManagedListState {
  constructor(client, debounceMs = DEFAULT_LIST_CHANGED_DEBOUNCE_MS) {
    super(client, {
      listMethod: "resources/templates/list",
      changeEvent: "resourceTemplatesChange",
      listChangedEvent: "resourceTemplatesListChanged",
      // Templates are gated on the broader `resources` capability.
      capabilityKey: "resources",
      // No paged counterpart (templates aren't paginated in the UI), so they
      // must still aggregate on connect even in paginated mode (#1721).
      deferWhenPaginated: false,
      supportsIndicator: false,
      debounceMs,
      fetchAll: async (c, cacheMode, metadata) => {
        const result = await c.listAllResourceTemplates({
          cacheMode,
          metadata
        });
        return result.resourceTemplates;
      }
    });
  }
  getResourceTemplates() {
    return this.getItems();
  }
};

// ../../core/mcp/state/managedPromptsState.ts
var ManagedPromptsState = class extends ManagedListState {
  constructor(client, debounceMs = DEFAULT_LIST_CHANGED_DEBOUNCE_MS) {
    super(client, {
      listMethod: "prompts/list",
      changeEvent: "promptsChange",
      listChangedEvent: "promptsListChanged",
      capabilityKey: "prompts",
      deferWhenPaginated: true,
      supportsIndicator: true,
      debounceMs,
      fetchAll: async (c, cacheMode, metadata) => {
        const result = await c.listAllPrompts({ cacheMode, metadata });
        return result.prompts;
      }
    });
  }
  getPrompts() {
    return this.getItems();
  }
};

// ../../core/react/useInspectorClient.ts
import { useState, useEffect, useCallback } from "react";
var EMPTY_CLIENT_CAPABILITIES = Object.freeze({});
function useInspectorClient(inspectorClient) {
  const [status, setStatus] = useState(
    inspectorClient?.getStatus() ?? "disconnected"
  );
  const [capabilities, setCapabilities] = useState(inspectorClient?.getCapabilities());
  const [serverInfo, setServerInfo] = useState(
    inspectorClient?.getServerInfo()
  );
  const [instructions, setInstructions] = useState(
    inspectorClient?.getInstructions()
  );
  const [protocolVersion, setProtocolVersion] = useState(
    inspectorClient?.getProtocolVersion()
  );
  const [protocolEra, setProtocolEra] = useState(
    inspectorClient?.getProtocolEra()
  );
  const [discoverResult, setDiscoverResult] = useState(inspectorClient?.getDiscoverResult());
  const [excludedTools, setExcludedTools] = useState(
    inspectorClient?.getExcludedTools() ?? []
  );
  const [lastError, setLastError] = useState(void 0);
  useEffect(() => {
    if (!inspectorClient) {
      setStatus("disconnected");
      setCapabilities(void 0);
      setServerInfo(void 0);
      setInstructions(void 0);
      setProtocolVersion(void 0);
      setProtocolEra(void 0);
      setDiscoverResult(void 0);
      setExcludedTools([]);
      setLastError(void 0);
      return;
    }
    setStatus(inspectorClient.getStatus());
    setCapabilities(inspectorClient.getCapabilities());
    setServerInfo(inspectorClient.getServerInfo());
    setInstructions(inspectorClient.getInstructions());
    setProtocolVersion(inspectorClient.getProtocolVersion());
    setProtocolEra(inspectorClient.getProtocolEra());
    setDiscoverResult(inspectorClient.getDiscoverResult());
    setExcludedTools(inspectorClient.getExcludedTools());
    setLastError(void 0);
    const onStatusChange = (event) => {
      setStatus(event.detail);
      if (event.detail === "connecting") {
        setLastError(void 0);
      }
    };
    const onError = (event) => {
      setLastError(event.detail.message);
    };
    const onCapabilitiesChange = (event) => {
      setCapabilities(event.detail);
    };
    const onServerInfoChange = (event) => {
      setServerInfo(event.detail);
    };
    const onInstructionsChange = (event) => {
      setInstructions(event.detail);
    };
    const onProtocolVersionChange = (event) => {
      setProtocolVersion(event.detail);
    };
    const onProtocolEraChange = (event) => {
      setProtocolEra(event.detail);
    };
    const onDiscoverResultChange = (event) => {
      setDiscoverResult(event.detail);
    };
    const onExcludedToolsChange = (event) => {
      setExcludedTools(event.detail);
    };
    inspectorClient.addEventListener("statusChange", onStatusChange);
    inspectorClient.addEventListener("error", onError);
    inspectorClient.addEventListener(
      "capabilitiesChange",
      onCapabilitiesChange
    );
    inspectorClient.addEventListener("serverInfoChange", onServerInfoChange);
    inspectorClient.addEventListener(
      "instructionsChange",
      onInstructionsChange
    );
    inspectorClient.addEventListener(
      "protocolVersionChange",
      onProtocolVersionChange
    );
    inspectorClient.addEventListener("protocolEraChange", onProtocolEraChange);
    inspectorClient.addEventListener(
      "discoverResultChange",
      onDiscoverResultChange
    );
    inspectorClient.addEventListener(
      "excludedToolsChange",
      onExcludedToolsChange
    );
    return () => {
      inspectorClient.removeEventListener("statusChange", onStatusChange);
      inspectorClient.removeEventListener("error", onError);
      inspectorClient.removeEventListener(
        "capabilitiesChange",
        onCapabilitiesChange
      );
      inspectorClient.removeEventListener(
        "serverInfoChange",
        onServerInfoChange
      );
      inspectorClient.removeEventListener(
        "instructionsChange",
        onInstructionsChange
      );
      inspectorClient.removeEventListener(
        "protocolVersionChange",
        onProtocolVersionChange
      );
      inspectorClient.removeEventListener(
        "protocolEraChange",
        onProtocolEraChange
      );
      inspectorClient.removeEventListener(
        "discoverResultChange",
        onDiscoverResultChange
      );
      inspectorClient.removeEventListener(
        "excludedToolsChange",
        onExcludedToolsChange
      );
    };
  }, [inspectorClient]);
  const connect = useCallback(async () => {
    if (!inspectorClient) return;
    await inspectorClient.connect();
  }, [inspectorClient]);
  const disconnect = useCallback(async () => {
    if (!inspectorClient) return;
    await inspectorClient.disconnect();
  }, [inspectorClient]);
  return {
    status,
    capabilities,
    // Read lazily on every render rather than subscribed: client capabilities
    // are built once in InspectorClient's constructor (from `sample`, `elicit`,
    // `roots`, `receiverTasks`) and never mutate during a session, so there's
    // no event to subscribe to. The module-scope frozen empty object is the
    // stable fallback when no client is attached.
    clientCapabilities: inspectorClient?.getClientCapabilities() ?? EMPTY_CLIENT_CAPABILITIES,
    serverInfo,
    instructions,
    protocolVersion,
    protocolEra,
    discoverResult,
    excludedTools,
    lastError,
    appRendererClient: inspectorClient?.getAppRendererClient() ?? null,
    connect,
    disconnect
  };
}

// ../../core/react/useManagedTools.ts
import { useState as useState2, useEffect as useEffect2, useCallback as useCallback3 } from "react";

// ../../core/react/useManagedListError.ts
import { useCallback as useCallback2, useSyncExternalStore } from "react";
function useManagedListError(state) {
  const subscribe = useCallback2(
    (onStoreChange) => {
      if (!state) return () => {
      };
      const listener = () => onStoreChange();
      state.addEventListener("errorChange", listener);
      return () => {
        state.removeEventListener("errorChange", listener);
      };
    },
    [state]
  );
  const getSnapshot = useCallback2(() => state?.getError() ?? null, [state]);
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
}

// ../../core/react/useManagedTools.ts
function useManagedTools(client, managedToolsState) {
  const [tools, setTools] = useState2(
    managedToolsState?.getTools() ?? []
  );
  const [listChanged, setListChanged] = useState2(
    managedToolsState?.getListChanged() ?? false
  );
  useEffect2(() => {
    if (!managedToolsState) {
      setTools([]);
      setListChanged(false);
      return;
    }
    setTools(managedToolsState.getTools());
    setListChanged(managedToolsState.getListChanged());
    const onToolsChange = (event) => {
      setTools(event.detail);
    };
    const onListChangedChange = (event) => {
      setListChanged(event.detail);
    };
    managedToolsState.addEventListener("toolsChange", onToolsChange);
    managedToolsState.addEventListener(
      "listChangedChange",
      onListChangedChange
    );
    return () => {
      managedToolsState.removeEventListener("toolsChange", onToolsChange);
      managedToolsState.removeEventListener(
        "listChangedChange",
        onListChangedChange
      );
    };
  }, [managedToolsState]);
  const error = useManagedListError(managedToolsState);
  const refresh = useCallback3(async () => {
    if (!managedToolsState || !client) return [];
    managedToolsState.clearListChanged();
    const next = await managedToolsState.refresh(void 0, "refresh");
    setTools(next);
    return next;
  }, [client, managedToolsState]);
  const clearListChanged = useCallback3(() => {
    managedToolsState?.clearListChanged();
  }, [managedToolsState]);
  return { tools, error, listChanged, refresh, clearListChanged };
}

// ../../core/react/useManagedResources.ts
import { useState as useState3, useEffect as useEffect3, useCallback as useCallback4 } from "react";
function useManagedResources(client, managedResourcesState) {
  const [resources, setResources] = useState3(
    managedResourcesState?.getResources() ?? []
  );
  const [listChanged, setListChanged] = useState3(
    managedResourcesState?.getListChanged() ?? false
  );
  useEffect3(() => {
    if (!managedResourcesState) {
      setResources([]);
      setListChanged(false);
      return;
    }
    setResources(managedResourcesState.getResources());
    setListChanged(managedResourcesState.getListChanged());
    const onResourcesChange = (event) => {
      setResources(event.detail);
    };
    const onListChangedChange = (event) => {
      setListChanged(event.detail);
    };
    managedResourcesState.addEventListener(
      "resourcesChange",
      onResourcesChange
    );
    managedResourcesState.addEventListener(
      "listChangedChange",
      onListChangedChange
    );
    return () => {
      managedResourcesState.removeEventListener(
        "resourcesChange",
        onResourcesChange
      );
      managedResourcesState.removeEventListener(
        "listChangedChange",
        onListChangedChange
      );
    };
  }, [managedResourcesState]);
  const error = useManagedListError(managedResourcesState);
  const refresh = useCallback4(async () => {
    if (!managedResourcesState || !client) return [];
    managedResourcesState.clearListChanged();
    const next = await managedResourcesState.refresh(void 0, "refresh");
    setResources(next);
    return next;
  }, [client, managedResourcesState]);
  const clearListChanged = useCallback4(() => {
    managedResourcesState?.clearListChanged();
  }, [managedResourcesState]);
  return { resources, error, listChanged, refresh, clearListChanged };
}

// ../../core/react/useManagedResourceTemplates.ts
import { useState as useState4, useEffect as useEffect4, useCallback as useCallback5 } from "react";
function useManagedResourceTemplates(client, managedResourceTemplatesState) {
  const [resourceTemplates, setResourceTemplates] = useState4(managedResourceTemplatesState?.getResourceTemplates() ?? []);
  useEffect4(() => {
    if (!managedResourceTemplatesState) {
      setResourceTemplates([]);
      return;
    }
    setResourceTemplates(managedResourceTemplatesState.getResourceTemplates());
    const onResourceTemplatesChange = (event) => {
      setResourceTemplates(event.detail);
    };
    managedResourceTemplatesState.addEventListener(
      "resourceTemplatesChange",
      onResourceTemplatesChange
    );
    return () => {
      managedResourceTemplatesState.removeEventListener(
        "resourceTemplatesChange",
        onResourceTemplatesChange
      );
    };
  }, [managedResourceTemplatesState]);
  const error = useManagedListError(managedResourceTemplatesState);
  const refresh = useCallback5(async () => {
    if (!managedResourceTemplatesState || !client) return [];
    const next = await managedResourceTemplatesState.refresh(
      void 0,
      "refresh"
    );
    setResourceTemplates(next);
    return next;
  }, [client, managedResourceTemplatesState]);
  return { resourceTemplates, error, refresh };
}

// ../../core/react/useManagedPrompts.ts
import { useState as useState5, useEffect as useEffect5, useCallback as useCallback6 } from "react";
function useManagedPrompts(client, managedPromptsState) {
  const [prompts, setPrompts] = useState5(
    managedPromptsState?.getPrompts() ?? []
  );
  const [listChanged, setListChanged] = useState5(
    managedPromptsState?.getListChanged() ?? false
  );
  useEffect5(() => {
    if (!managedPromptsState) {
      setPrompts([]);
      setListChanged(false);
      return;
    }
    setPrompts(managedPromptsState.getPrompts());
    setListChanged(managedPromptsState.getListChanged());
    const onPromptsChange = (event) => {
      setPrompts(event.detail);
    };
    const onListChangedChange = (event) => {
      setListChanged(event.detail);
    };
    managedPromptsState.addEventListener("promptsChange", onPromptsChange);
    managedPromptsState.addEventListener(
      "listChangedChange",
      onListChangedChange
    );
    return () => {
      managedPromptsState.removeEventListener("promptsChange", onPromptsChange);
      managedPromptsState.removeEventListener(
        "listChangedChange",
        onListChangedChange
      );
    };
  }, [managedPromptsState]);
  const error = useManagedListError(managedPromptsState);
  const refresh = useCallback6(async () => {
    if (!managedPromptsState || !client) return [];
    managedPromptsState.clearListChanged();
    const next = await managedPromptsState.refresh(void 0, "refresh");
    setPrompts(next);
    return next;
  }, [client, managedPromptsState]);
  const clearListChanged = useCallback6(() => {
    managedPromptsState?.clearListChanged();
  }, [managedPromptsState]);
  return { prompts, error, listChanged, refresh, clearListChanged };
}

// ../../core/react/useMessageLog.ts
import { useState as useState6, useEffect as useEffect6 } from "react";
function useMessageLog(messageLogState) {
  const [messages, setMessages] = useState6(
    messageLogState?.getMessages() ?? []
  );
  useEffect6(() => {
    if (!messageLogState) {
      setMessages([]);
      return;
    }
    setMessages(messageLogState.getMessages());
    const onMessagesChange = (event) => {
      setMessages(event.detail);
    };
    messageLogState.addEventListener("messagesChange", onMessagesChange);
    return () => {
      messageLogState.removeEventListener("messagesChange", onMessagesChange);
    };
  }, [messageLogState]);
  return { messages };
}

// ../../core/react/useFetchRequestLog.ts
import { useState as useState7, useEffect as useEffect7 } from "react";
function useFetchRequestLog(fetchRequestLogState) {
  const [fetchRequests, setFetchRequests] = useState7(
    fetchRequestLogState?.getFetchRequests() ?? []
  );
  useEffect7(() => {
    if (!fetchRequestLogState) {
      setFetchRequests([]);
      return;
    }
    setFetchRequests(fetchRequestLogState.getFetchRequests());
    const onFetchRequestsChange = (event) => {
      setFetchRequests(event.detail);
    };
    fetchRequestLogState.addEventListener(
      "fetchRequestsChange",
      onFetchRequestsChange
    );
    return () => {
      fetchRequestLogState.removeEventListener(
        "fetchRequestsChange",
        onFetchRequestsChange
      );
    };
  }, [fetchRequestLogState]);
  return { fetchRequests };
}

// ../../core/react/useStderrLog.ts
import { useState as useState8, useEffect as useEffect8 } from "react";
function useStderrLog(stderrLogState) {
  const [stderrLogs, setStderrLogs] = useState8(
    stderrLogState?.getStderrLogs() ?? []
  );
  useEffect8(() => {
    if (!stderrLogState) {
      setStderrLogs([]);
      return;
    }
    setStderrLogs(stderrLogState.getStderrLogs());
    const onStderrLogsChange = (event) => {
      setStderrLogs(event.detail);
    };
    stderrLogState.addEventListener("stderrLogsChange", onStderrLogsChange);
    return () => {
      stderrLogState.removeEventListener(
        "stderrLogsChange",
        onStderrLogsChange
      );
    };
  }, [stderrLogState]);
  return { stderrLogs };
}

// ../../core/auth/issuerBinding.ts
import { AuthorizationServerMismatchError } from "@modelcontextprotocol/client";
var MISSING_DISCOVERY_STATE_PHRASE = "discoveryState was not available";
function isAuthorizationServerMismatchShape(err) {
  if (err === null || typeof err !== "object") {
    return false;
  }
  const candidate = err;
  if (typeof candidate.recordedIssuer !== "string" || typeof candidate.currentIssuer !== "string") {
    return false;
  }
  return AuthorizationServerMismatchError.isInstance(err) || candidate.name === "AuthorizationServerMismatchError";
}
function isMissingDiscoveryStatePlaceholder(recordedIssuer) {
  if (recordedIssuer.includes(MISSING_DISCOVERY_STATE_PHRASE)) {
    return true;
  }
  try {
    const url = new URL(recordedIssuer);
    return url.protocol !== "http:" && url.protocol !== "https:";
  } catch {
    return true;
  }
}
function findIssuerBindingFailure(err) {
  return findIssuerBindingFailureDeep(err, /* @__PURE__ */ new Set());
}
function findIssuerBindingFailureDeep(err, seen) {
  if (err === null || typeof err !== "object" || seen.has(err)) {
    return void 0;
  }
  seen.add(err);
  if (isAuthorizationServerMismatchShape(err)) {
    return classifyAuthorizationServerMismatch(err);
  }
  const nested = findIssuerBindingFailureDeep(
    err.cause,
    seen
  );
  if (nested) {
    return nested;
  }
  const data = err.data;
  if (data !== null && typeof data === "object") {
    return findIssuerBindingFailureDeep(
      data.cause,
      seen
    );
  }
  return void 0;
}
function classifyAuthorizationServerMismatch(err) {
  if (isMissingDiscoveryStatePlaceholder(err.recordedIssuer)) {
    return {
      kind: "lost_authorization_state",
      currentIssuer: err.currentIssuer
    };
  }
  return {
    kind: "issuer_mismatch",
    recordedIssuer: err.recordedIssuer,
    currentIssuer: err.currentIssuer
  };
}

// ../../core/node/version.ts
import { readFileSync as readFileSync2 } from "fs";
import { dirname as dirname3, join as join2, parse } from "path";
import { fileURLToPath } from "url";
var ROOT_PACKAGE_NAME = "@modelcontextprotocol/inspector";
function readInspectorVersion(callerUrl) {
  const startDir = dirname3(fileURLToPath(callerUrl));
  const { root } = parse(startDir);
  let dir = startDir;
  for (; ; ) {
    let manifest;
    try {
      manifest = JSON.parse(
        readFileSync2(join2(dir, "package.json"), "utf-8")
      );
    } catch {
    }
    if (manifest?.name === ROOT_PACKAGE_NAME && manifest.version) {
      return manifest.version;
    }
    if (dir === root) break;
    dir = dirname3(dir);
  }
  throw new Error(
    `Could not locate the ${ROOT_PACKAGE_NAME} root package.json above ${startDir}`
  );
}

// ../../core/auth/oauth-storage.ts
import {
  OAuthClientInformationSchema,
  OAuthTokensSchema as OAuthTokensSchema2
} from "@modelcontextprotocol/core";
function withIssuer(value, issuer) {
  return issuer === void 0 ? value : { ...value, issuer };
}
var OAuthStorageBase = class {
  loaded = false;
  loadPromise;
  /** Serializes persist writes so concurrent mutators cannot reorder POSTs. */
  persistQueue = Promise.resolve();
  memory;
  backend;
  constructor(memory, backend) {
    this.memory = memory;
    this.backend = backend;
  }
  load() {
    if (!this.loadPromise) {
      this.loadPromise = this.doLoad();
    }
    return this.loadPromise;
  }
  async doLoad() {
    if (this.loaded) {
      return;
    }
    const snapshot = await this.backend.read();
    if (snapshot) {
      this.memory.replace(snapshot);
    }
    this.loaded = true;
  }
  async ensureLoaded() {
    await this.load();
  }
  async persist() {
    const snapshot = this.memory.snapshot();
    const prior = this.persistQueue;
    const tracked = prior.catch(() => {
    }).then(() => this.backend.write(snapshot));
    this.persistQueue = tracked;
    await tracked;
  }
  /**
   * Resolve the AS-`issuer` key whose credentials answer a read. An explicit
   * `issuer` (from the SDK's `ctx.issuer`) wins; otherwise the most-recently-saved
   * `activeIssuer` answers the transport's ctx-less per-request bearer read.
   */
  resolveReadIssuer(state, issuer) {
    return issuer ?? state.activeIssuer;
  }
  /** Read the issuer-bound slot for a read, if one exists. */
  issuerSlot(state, issuer) {
    const key = this.resolveReadIssuer(state, issuer);
    return key ? state.byIssuer?.[key] : void 0;
  }
  /**
   * Read-modify-write the `byIssuer[issuer]` slot, set `activeIssuer`, and clear
   * the given legacy top-level fallback fields (lazy migration — the fallback is
   * promoted into the keyed slot on first issuer-stamped save).
   */
  updateIssuerSlot(serverUrl, issuer, updates, clearLegacy, setActive = true) {
    const state = this.memory.getState().getServerState(serverUrl);
    const byIssuer = {
      ...state.byIssuer,
      [issuer]: { ...state.byIssuer?.[issuer], ...updates }
    };
    this.memory.getState().setServerState(serverUrl, {
      byIssuer,
      ...setActive && { activeIssuer: issuer },
      ...clearLegacy
    });
  }
  /** Apply `fn` to every issuer slot (used by issuer-agnostic clears). */
  mapIssuerSlots(state, fn) {
    const byIssuer = {};
    for (const [key, slot] of Object.entries(state.byIssuer ?? {})) {
      byIssuer[key] = { ...slot, ...fn(slot) };
    }
    return byIssuer;
  }
  async getClientInformation(serverUrl, isPreregistered, issuer) {
    await this.ensureLoaded();
    const state = this.memory.getState().getServerState(serverUrl);
    if (isPreregistered) {
      const prereg = state.preregisteredClientInformation;
      return prereg ? await OAuthClientInformationSchema.parseAsync(prereg) : void 0;
    }
    const slot = this.issuerSlot(state, issuer);
    const fromSlot = slot?.clientInformation;
    const clientInfo = fromSlot ?? state.clientInformation;
    if (!clientInfo) {
      return void 0;
    }
    const parsed = await OAuthClientInformationSchema.parseAsync(clientInfo);
    return withIssuer(
      parsed,
      fromSlot ? this.resolveReadIssuer(state, issuer) : void 0
    );
  }
  async getClientRegistrationKind(serverUrl, issuer) {
    await this.ensureLoaded();
    const state = this.memory.getState().getServerState(serverUrl);
    return this.issuerSlot(state, issuer)?.clientRegistrationKind ?? state.clientRegistrationKind;
  }
  async saveClientInformation(serverUrl, clientInformation, options) {
    await this.ensureLoaded();
    if (options.issuer !== void 0) {
      this.updateIssuerSlot(
        serverUrl,
        options.issuer,
        {
          clientInformation,
          clientRegistrationKind: options.registrationKind
        },
        { clientInformation: void 0, clientRegistrationKind: void 0 }
      );
    } else {
      this.memory.getState().setServerState(serverUrl, {
        clientInformation,
        clientRegistrationKind: options.registrationKind
      });
    }
    await this.persist();
  }
  async savePreregisteredClientInformation(serverUrl, clientInformation) {
    await this.ensureLoaded();
    this.memory.getState().setServerState(serverUrl, {
      preregisteredClientInformation: clientInformation,
      clientRegistrationKind: "static"
    });
    await this.persist();
  }
  async clearClientInformation(serverUrl, isPreregistered, issuer) {
    await this.ensureLoaded();
    if (isPreregistered) {
      this.memory.getState().setServerState(serverUrl, {
        preregisteredClientInformation: void 0
      });
      await this.persist();
      return;
    }
    if (issuer !== void 0) {
      this.updateIssuerSlot(
        serverUrl,
        issuer,
        { clientInformation: void 0, clientRegistrationKind: void 0 },
        {},
        false
      );
    } else {
      const state = this.memory.getState().getServerState(serverUrl);
      const byIssuer = this.mapIssuerSlots(state, () => ({
        clientInformation: void 0,
        clientRegistrationKind: void 0
      }));
      this.memory.getState().setServerState(serverUrl, {
        byIssuer,
        clientInformation: void 0,
        clientRegistrationKind: void 0
      });
    }
    await this.persist();
  }
  async getTokens(serverUrl, issuer) {
    await this.ensureLoaded();
    const state = this.memory.getState().getServerState(serverUrl);
    const slot = this.issuerSlot(state, issuer);
    const fromSlot = slot?.tokens;
    const tokens = fromSlot ?? state.tokens;
    if (!tokens) {
      return void 0;
    }
    const parsed = await OAuthTokensSchema2.parseAsync(tokens);
    return withIssuer(
      parsed,
      fromSlot ? this.resolveReadIssuer(state, issuer) : void 0
    );
  }
  async saveTokens(serverUrl, tokens, options) {
    await this.ensureLoaded();
    if (options?.issuer !== void 0) {
      this.updateIssuerSlot(
        serverUrl,
        options.issuer,
        { tokens },
        {
          tokens: void 0,
          ...options.enterpriseManaged === true && {
            enterpriseManaged: true
          }
        }
      );
    } else {
      this.memory.getState().setServerState(serverUrl, {
        tokens,
        ...options?.enterpriseManaged === true && { enterpriseManaged: true }
      });
    }
    await this.persist();
  }
  async clearTokens(serverUrl, issuer) {
    await this.ensureLoaded();
    if (issuer !== void 0) {
      this.updateIssuerSlot(
        serverUrl,
        issuer,
        { tokens: void 0 },
        {},
        false
      );
    } else {
      const state = this.memory.getState().getServerState(serverUrl);
      const byIssuer = this.mapIssuerSlots(state, () => ({
        tokens: void 0
      }));
      this.memory.getState().setServerState(serverUrl, { byIssuer, tokens: void 0 });
    }
    await this.persist();
  }
  async getCodeVerifier(serverUrl) {
    await this.ensureLoaded();
    const state = this.memory.getState().getServerState(serverUrl);
    return state.codeVerifier;
  }
  async saveCodeVerifier(serverUrl, codeVerifier) {
    await this.ensureLoaded();
    this.memory.getState().setServerState(serverUrl, { codeVerifier });
    await this.persist();
  }
  async clearCodeVerifier(serverUrl) {
    await this.ensureLoaded();
    this.memory.getState().setServerState(serverUrl, { codeVerifier: void 0 });
    await this.persist();
  }
  async getScope(serverUrl) {
    await this.ensureLoaded();
    const state = this.memory.getState().getServerState(serverUrl);
    return state.scope;
  }
  async saveScope(serverUrl, scope) {
    await this.ensureLoaded();
    this.memory.getState().setServerState(serverUrl, { scope });
    await this.persist();
  }
  async clearScope(serverUrl) {
    await this.ensureLoaded();
    this.memory.getState().setServerState(serverUrl, { scope: void 0 });
    await this.persist();
  }
  async getServerMetadata(serverUrl) {
    await this.ensureLoaded();
    const state = this.memory.getState().getServerState(serverUrl);
    return state.serverMetadata || null;
  }
  async saveServerMetadata(serverUrl, metadata) {
    await this.ensureLoaded();
    this.memory.getState().setServerState(serverUrl, { serverMetadata: metadata });
    await this.persist();
  }
  async clearServerMetadata(serverUrl) {
    await this.ensureLoaded();
    this.memory.getState().setServerState(serverUrl, { serverMetadata: void 0 });
    await this.persist();
  }
  async getDiscoveryState(serverUrl) {
    await this.ensureLoaded();
    return this.memory.getState().getServerState(serverUrl).discoveryState;
  }
  async saveDiscoveryState(serverUrl, state) {
    await this.ensureLoaded();
    this.memory.getState().setServerState(serverUrl, { discoveryState: state });
    await this.persist();
  }
  async clearDiscoveryState(serverUrl) {
    await this.ensureLoaded();
    this.memory.getState().setServerState(serverUrl, { discoveryState: void 0 });
    await this.persist();
  }
  async clear(serverUrl) {
    await this.ensureLoaded();
    this.memory.getState().clearServerState(serverUrl);
    await this.persist();
  }
  async getIdpSession(issuer) {
    await this.ensureLoaded();
    const session = this.memory.getState().getIdpSession(issuer);
    if (!session.idToken && !session.refreshToken && session.idTokenExpiresAt === void 0) {
      return void 0;
    }
    return session;
  }
  async saveIdpSession(issuer, session) {
    await this.ensureLoaded();
    this.memory.getState().setIdpSession(issuer, session);
    await this.persist();
  }
  async clearIdpSession(issuer) {
    await this.ensureLoaded();
    this.memory.getState().clearIdpSession(issuer);
    await this.persist();
  }
  async clearEnterpriseManagedResourceServers() {
    await this.ensureLoaded();
    this.memory.getState().clearEnterpriseManagedResourceServers();
    await this.persist();
  }
};

// ../../core/auth/store.ts
var OAuthMemoryStore = class {
  servers = {};
  idpSessions = {};
  constructor(initial) {
    if (initial) {
      this.replace(initial);
    }
  }
  getState() {
    return {
      servers: this.servers,
      idpSessions: this.idpSessions,
      getServerState: (serverUrl) => {
        return this.servers[serverUrl] || {};
      },
      setServerState: (serverUrl, updates) => {
        this.servers = {
          ...this.servers,
          [serverUrl]: {
            ...this.servers[serverUrl],
            ...updates
          }
        };
      },
      clearServerState: (serverUrl) => {
        const rest = { ...this.servers };
        delete rest[serverUrl];
        this.servers = rest;
      },
      getIdpSession: (issuer) => {
        return this.idpSessions[issuer] || {};
      },
      setIdpSession: (issuer, updates) => {
        this.idpSessions = {
          ...this.idpSessions,
          [issuer]: {
            ...this.idpSessions[issuer],
            ...updates
          }
        };
      },
      clearIdpSession: (issuer) => {
        const rest = { ...this.idpSessions };
        delete rest[issuer];
        this.idpSessions = rest;
      },
      clearEnterpriseManagedResourceServers: () => {
        const rest = { ...this.servers };
        for (const [url, serverState] of Object.entries(this.servers)) {
          if (serverState.enterpriseManaged === true) {
            delete rest[url];
          }
        }
        this.servers = rest;
      }
    };
  }
  snapshot() {
    return {
      servers: { ...this.servers },
      idpSessions: { ...this.idpSessions }
    };
  }
  replace(snapshot) {
    this.servers = { ...snapshot.servers ?? {} };
    this.idpSessions = { ...snapshot.idpSessions ?? {} };
  }
};

// ../../core/auth/oauth-persist.ts
function isRecord2(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function snapshotFromPayload(payload) {
  return {
    servers: payload.servers ?? {},
    idpSessions: payload.idpSessions ?? {}
  };
}
function parseOAuthPersistBlob(raw) {
  if (raw === null || raw === void 0) {
    return null;
  }
  const parsed = typeof raw === "string" ? raw ? parseStore(raw) : null : raw;
  if (!isRecord2(parsed)) {
    return null;
  }
  if (isRecord2(parsed.state) && "version" in parsed) {
    return snapshotFromPayload(parsed.state);
  }
  if ("servers" in parsed || "idpSessions" in parsed) {
    return snapshotFromPayload(parsed);
  }
  return null;
}
function serializeOAuthPersistBlob(snapshot) {
  return serializeStore(snapshot);
}

// ../../core/auth/node/oauth-persist-file.ts
function createFileOAuthPersistBackend(options) {
  return {
    async read() {
      const raw = await readStoreFile(options.filePath);
      return parseOAuthPersistBlob(raw);
    },
    async write(snapshot) {
      await writeStoreFile(
        options.filePath,
        serializeOAuthPersistBlob(snapshot)
      );
    },
    async remove() {
      await deleteStoreFile(options.filePath);
    }
  };
}

// ../../core/auth/node/storage-node.ts
var DEFAULT_STATE_PATH = getStoreFilePath(getDefaultStorageDir(), "oauth");
function getStateFilePath(customPath) {
  if (customPath) return customPath;
  if (process.env.MCP_INSPECTOR_OAUTH_STATE_PATH) {
    return process.env.MCP_INSPECTOR_OAUTH_STATE_PATH;
  }
  if (process.env.MCP_STORAGE_DIR) {
    return getStoreFilePath(process.env.MCP_STORAGE_DIR, "oauth");
  }
  return DEFAULT_STATE_PATH;
}
var memoryCache = /* @__PURE__ */ new Map();
function getSharedMemory(stateFilePath) {
  const key = getStateFilePath(stateFilePath);
  let memory = memoryCache.get(key);
  if (!memory) {
    memory = new OAuthMemoryStore();
    memoryCache.set(key, memory);
  }
  return memory;
}
var NodeOAuthStorage = class extends OAuthStorageBase {
  /**
   * @param storagePath - Optional path to state file. Default: ~/.mcp-inspector/storage/oauth.json
   */
  constructor(storagePath) {
    const filePath = getStateFilePath(storagePath);
    super(
      getSharedMemory(storagePath),
      createFileOAuthPersistBackend({ filePath })
    );
  }
};

// ../../core/auth/node/oauth-callback-server.ts
import { createServer } from "http";
var DEFAULT_HOSTNAME = "127.0.0.1";
var DEFAULT_CALLBACK_PATH = "/oauth/callback";
var SUCCESS_HTML = `<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>OAuth complete</title></head>
<body><p>OAuth complete. You can close this window.</p></body>
</html>`;
function errorHtml(message) {
  return `<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>OAuth error</title></head>
<body><p>OAuth failed: ${escapeHtml(message)}</p></body>
</html>`;
}
function escapeHtml(s) {
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
var OAuthCallbackServer = class {
  server = null;
  port = 0;
  callbackPath = DEFAULT_CALLBACK_PATH;
  handled = false;
  onCallback;
  onError;
  /**
   * Start the server. Port defaults to 0 (random) when omitted; TUI/CLI callers
   * pass the resolved port from {@link parseRunnerOAuthCallbackUrl} (6276 by default).
   * Returns port and redirectUrl for use as oauth.redirectUrl.
   */
  async start(options = {}) {
    const {
      port = 0,
      hostname = DEFAULT_HOSTNAME,
      path: path3 = DEFAULT_CALLBACK_PATH,
      onCallback,
      onError
    } = options;
    if (!path3.startsWith("/")) {
      return Promise.reject(
        new Error("Callback path must start with '/' (absolute path)")
      );
    }
    this.onCallback = onCallback;
    this.onError = onError;
    this.handled = false;
    this.callbackPath = path3;
    return new Promise((resolve4, reject) => {
      this.server = createServer((req, res) => this.handleRequest(req, res));
      this.server.on("error", reject);
      this.server.listen(port, hostname, () => {
        const a = this.server.address();
        if (!a || typeof a === "string") {
          reject(new Error("Failed to get server address"));
          return;
        }
        this.port = a.port;
        resolve4({
          port: this.port,
          redirectUrl: buildRedirectUrl(hostname, this.port, path3)
        });
      });
    });
  }
  /**
   * Stop the server. Idempotent.
   */
  async stop() {
    if (!this.server) return;
    await new Promise((resolve4) => {
      this.server.close(() => resolve4());
    });
    this.server = null;
  }
  handleRequest(req, res) {
    const needJson = req.headers["accept"]?.includes("application/json");
    const send = (status, body, contentType = "text/html; charset=utf-8") => {
      res.writeHead(status, { "Content-Type": contentType });
      res.end(body);
    };
    if (req.method !== "GET") {
      send(405, needJson ? '{"error":"Method Not Allowed"}' : SUCCESS_HTML);
      return;
    }
    let pathname;
    let search;
    let state;
    try {
      const u = new URL(
        /* v8 ignore next -- defensive: Node always populates req.url for an
           HTTP request, so the `?? ""` fallback is never taken. */
        req.url ?? "",
        "http://placeholder"
      );
      pathname = u.pathname;
      search = u.search;
      state = u.searchParams.get("state") ?? void 0;
    } catch {
      send(400, needJson ? '{"error":"Bad Request"}' : SUCCESS_HTML);
      return;
    }
    if (pathname !== this.callbackPath) {
      send(404, needJson ? '{"error":"Not Found"}' : SUCCESS_HTML);
      return;
    }
    if (this.handled) {
      send(
        409,
        needJson ? '{"error":"Callback already handled"}' : SUCCESS_HTML
      );
      return;
    }
    const params = parseOAuthCallbackParams(search);
    if (params.successful) {
      this.handled = true;
      const cb = this.onCallback;
      if (cb) {
        cb({ code: params.code, state, iss: params.iss }).then(() => {
          send(200, SUCCESS_HTML);
          void this.stop();
        }).catch((err) => {
          const msg2 = err instanceof Error ? err.message : String(err);
          this.onError?.({ error: "callback_error", error_description: msg2 });
          send(500, errorHtml(msg2));
          void this.stop();
        });
      } else {
        send(200, SUCCESS_HTML);
        void this.stop();
      }
      return;
    }
    this.handled = true;
    const msg = generateOAuthErrorDescription(params);
    this.onError?.({
      error: params.error,
      error_description: params.error_description ?? void 0
    });
    send(400, errorHtml(msg));
  }
};
function createOAuthCallbackServer() {
  return new OAuthCallbackServer();
}
function buildRedirectUrl(host, port, path3) {
  return `http://${formatHostForUrl(host)}:${port}${path3}`;
}

// ../../core/auth/node/runner-interactive-oauth.ts
var DEFAULT_RUNNER_INTERACTIVE_OAUTH_TIMEOUT_MS = 15 * 60 * 1e3;
function toRunnerOAuthError(err) {
  const failure = findIssuerBindingFailure(err);
  if (failure) {
    const copy = issuerBindingFailureCopy(failure);
    return new Error(`${copy.title}. ${copy.message}`, { cause: err });
  }
  return err instanceof Error ? err : new Error(String(err));
}
async function runRunnerInteractiveOAuth(options) {
  const createServer2 = options.createCallbackServer ?? createOAuthCallbackServer;
  const server = createServer2();
  let flowResolve;
  let flowReject;
  const flowDone = new Promise((resolve4, reject) => {
    flowResolve = resolve4;
    flowReject = reject;
  });
  let timeoutId;
  try {
    const { redirectUrl } = await server.start({
      hostname: options.callbackListen.hostname,
      port: options.callbackListen.port,
      path: options.callbackListen.pathname,
      onCallback: async (params) => {
        try {
          await options.client.completeOAuthFlow(params.code, params.iss);
          flowResolve();
        } catch (err) {
          flowReject(toRunnerOAuthError(err));
        }
      },
      onError: (params) => {
        flowReject(
          new Error(
            /* v8 ignore next -- params.error is a required non-null string, so the "OAuth error" fallback is unreachable */
            params.error_description ?? params.error ?? "OAuth error"
          )
        );
      }
    });
    options.onCallbackServer?.(server);
    options.redirectUrlProvider.redirectUrl = redirectUrl;
    const timeoutMs = options.callbackTimeoutMs ?? DEFAULT_RUNNER_INTERACTIVE_OAUTH_TIMEOUT_MS;
    const waitForCallback = Promise.race([
      flowDone.finally(() => {
        if (timeoutId !== void 0) {
          clearTimeout(timeoutId);
        }
      }),
      new Promise((_, reject) => {
        timeoutId = setTimeout(() => {
          reject(
            new Error(
              `OAuth callback timed out after ${Math.round(timeoutMs / 1e3)}s`
            )
          );
        }, timeoutMs);
      })
    ]);
    if (options.authorizationUrl) {
      await options.client.beginInteractiveAuthorization(
        options.authorizationUrl
      );
      await waitForCallback;
    } else {
      const authUrl = await options.client.authenticate();
      if (authUrl !== void 0) {
        await waitForCallback;
      } else {
        return { kind: "already_authorized" };
      }
    }
    if (options.authChallenge) {
      const satisfied = await options.client.checkAuthChallengeSatisfied(
        options.authChallenge
      );
      if (!satisfied) {
        return {
          kind: "insufficient_scope",
          challenge: options.authChallenge
        };
      }
    }
    return { kind: "success" };
  } finally {
    if (timeoutId !== void 0) {
      clearTimeout(timeoutId);
    }
    await server.stop().catch(() => {
    });
  }
}

// src/logger.ts
import path2 from "path";
import pino from "pino";
var tuiLoggerInstance;
function getTuiLogger() {
  if (!tuiLoggerInstance) {
    const logDir = process.env.MCP_INSPECTOR_LOG_DIR ?? path2.join(
      process.env.HOME || process.env.USERPROFILE || ".",
      ".mcp-inspector"
    );
    const logPath = path2.join(logDir, "auth.log");
    tuiLoggerInstance = pino(
      {
        name: "mcp-inspector-tui",
        level: process.env.LOG_LEVEL ?? "info"
      },
      pino.destination({ dest: logPath, append: true, mkdir: true })
    );
  }
  return tuiLoggerInstance;
}

// src/utils/openUrl.ts
import open from "open";
async function openUrl(url) {
  await open(typeof url === "string" ? url : url.href);
}

// src/utils/tuiOAuth.ts
function isStepUpConfirmation2(challenge, settings) {
  return isStepUpConfirmation(challenge, {
    enterpriseManaged: settings?.enterpriseManaged
  });
}

// src/components/Tabs.tsx
import { Box, Text } from "ink";

// src/components/tabsConfig.ts
var tabs = [
  { id: "info", label: "Info", accelerator: "i" },
  { id: "auth", label: "Auth", accelerator: "a" },
  { id: "resources", label: "Resources", accelerator: "r" },
  { id: "prompts", label: "Prompts", accelerator: "m" },
  { id: "tools", label: "Tools", accelerator: "t" },
  { id: "messages", label: "Protocol", accelerator: "p" },
  { id: "requests", label: "Network", accelerator: "n" },
  { id: "logging", label: "Console", accelerator: "o" }
];

// src/components/Tabs.tsx
import { jsx, jsxs } from "react/jsx-runtime";
function splitLabelAtAccelerator(label, accelerator) {
  const idx = label.toLowerCase().indexOf(accelerator.toLowerCase());
  if (idx < 0) {
    return { before: "", accel: label.slice(0, 1), after: label.slice(1) };
  }
  return {
    before: label.slice(0, idx),
    accel: label.slice(idx, idx + 1),
    after: label.slice(idx + 1)
  };
}
function Tabs({
  activeTab,
  width,
  counts = {},
  focused = false,
  showAuth = true,
  showLogging = true,
  showRequests = false
}) {
  let visibleTabs = tabs;
  if (!showAuth) {
    visibleTabs = visibleTabs.filter((tab) => tab.id !== "auth");
  }
  if (!showLogging) {
    visibleTabs = visibleTabs.filter((tab) => tab.id !== "logging");
  }
  if (!showRequests) {
    visibleTabs = visibleTabs.filter((tab) => tab.id !== "requests");
  }
  return /* @__PURE__ */ jsx(
    Box,
    {
      width,
      flexShrink: 0,
      borderStyle: "single",
      borderTop: false,
      borderLeft: false,
      borderRight: false,
      borderBottom: true,
      flexDirection: "row",
      justifyContent: "space-between",
      flexWrap: "wrap",
      paddingX: 1,
      children: visibleTabs.map((tab) => {
        const isActive = activeTab === tab.id;
        const count = counts[tab.id];
        const countText = count !== void 0 ? ` (${count})` : "";
        const { before, accel, after } = splitLabelAtAccelerator(
          tab.label,
          tab.accelerator
        );
        return /* @__PURE__ */ jsx(Box, { flexShrink: 0, children: /* @__PURE__ */ jsxs(
          Text,
          {
            bold: isActive,
            ...isActive && focused ? {} : { color: isActive ? "cyan" : "gray" },
            backgroundColor: isActive && focused ? "yellow" : void 0,
            children: [
              isActive ? "\u25B6 " : "  ",
              before,
              /* @__PURE__ */ jsx(Text, { underline: true, children: accel }),
              after,
              countText
            ]
          }
        ) }, tab.id);
      })
    }
  );
}

// src/components/InfoTab.tsx
import { useRef as useRef3 } from "react";
import { Box as Box3, Text as Text2, useInput } from "ink";

// node_modules/ink-scroll-view/dist/index.js
import {
  useState as useState22,
  useRef as useRef2,
  forwardRef as forwardRef2,
  useImperativeHandle as useImperativeHandle2,
  useCallback as useCallback22
} from "react";
import {
  useState as useState9,
  useRef,
  forwardRef,
  useImperativeHandle,
  useLayoutEffect,
  useCallback as useCallback7,
  Children,
  isValidElement
} from "react";
import { Box as Box2, measureElement } from "ink";
import { jsx as jsx2 } from "react/jsx-runtime";
import { jsx as jsx22 } from "react/jsx-runtime";
var MeasurableItem = ({
  children,
  onMeasure,
  index,
  width,
  measureKey
}) => {
  const ref = useRef(null);
  useLayoutEffect(() => {
    if (ref.current) {
      const { height } = measureElement(ref.current);
      onMeasure(index, height);
    }
  }, [index, onMeasure, width, measureKey, children]);
  return /* @__PURE__ */ jsx2(Box2, { ref, flexShrink: 0, width: "100%", flexDirection: "column", children });
};
function useStateRef(initialValue) {
  const [state, setStateInternal] = useState9(initialValue);
  const ref = useRef(initialValue);
  const setState = useCallback7((update) => {
    const nextValue = typeof update === "function" ? update(ref.current) : update;
    ref.current = nextValue;
    setStateInternal(nextValue);
  }, []);
  const getState = useCallback7(() => ref.current, []);
  return [state, setState, getState];
}
var ControlledScrollView = forwardRef(
  ({
    scrollOffset,
    onViewportSizeChange,
    onContentHeightChange,
    onItemHeightChange,
    debug = false,
    children,
    ...boxProps
  }, ref) => {
    const [viewportSize, setViewportSize, getViewportSize] = useStateRef({
      height: 0,
      width: 0
    });
    const [contentHeight, setContentHeight, getContentHeight] = useStateRef(0);
    const [itemMeasureKeys, setItemMeasureKeys] = useState9({});
    const viewportRef = useRef(null);
    const contentRef = useRef(null);
    const prevContentHeightRef = useRef(0);
    useLayoutEffect(() => {
      if (contentHeight !== prevContentHeightRef.current) {
        onContentHeightChange?.(contentHeight, prevContentHeightRef.current);
        prevContentHeightRef.current = contentHeight;
      }
    }, [contentHeight, onContentHeightChange]);
    const itemHeightsRef = useRef({});
    const itemKeysRef = useRef([]);
    const itemOffsetsRef = useRef([]);
    const firstInvalidOffsetIndexRef = useRef(0);
    const handleItemMeasure = useCallback7(
      (index, height) => {
        const key = itemKeysRef.current[index] || index;
        if (itemHeightsRef.current[key] !== height) {
          const previousHeight = itemHeightsRef.current[key] || 0;
          itemHeightsRef.current = {
            ...itemHeightsRef.current,
            [key]: height
          };
          let newTotalHeight = 0;
          for (const itemKey of itemKeysRef.current) {
            newTotalHeight += itemHeightsRef.current[itemKey] || 0;
          }
          const currentHeight = getContentHeight();
          if (newTotalHeight !== currentHeight) {
            setContentHeight(newTotalHeight);
          }
          onItemHeightChange?.(index, height, previousHeight);
          firstInvalidOffsetIndexRef.current = Math.min(
            firstInvalidOffsetIndexRef.current,
            index + 1
          );
        }
      },
      [
        onItemHeightChange,
        onContentHeightChange,
        getContentHeight,
        setContentHeight
      ]
    );
    const measureViewport = useCallback7(() => {
      if (viewportRef.current) {
        const { width, height } = measureElement(viewportRef.current);
        const currentSize = getViewportSize();
        if (width !== currentSize.width || height !== currentSize.height) {
          onViewportSizeChange?.({ width, height }, currentSize);
          setViewportSize({ width, height });
        }
      }
    }, [viewportRef, onViewportSizeChange, getViewportSize, setViewportSize]);
    useLayoutEffect(() => {
      measureViewport();
    });
    const prevChildrenRef = useRef(null);
    if (prevChildrenRef.current !== children) {
      prevChildrenRef.current = children;
      const newItemKeys = [];
      const newItemHeights = {};
      Children.forEach(children, (child, index) => {
        if (!child) return;
        const key = isValidElement(child) ? child.key : null;
        const effectiveKey = key !== null ? key : index;
        newItemKeys[index] = effectiveKey;
        const itemHeight = itemHeightsRef.current[effectiveKey] || 0;
        newItemHeights[effectiveKey] = itemHeight;
      });
      itemHeightsRef.current = newItemHeights;
      itemKeysRef.current = newItemKeys;
      itemOffsetsRef.current = new Array(newItemKeys.length).fill(0);
      firstInvalidOffsetIndexRef.current = 0;
      let newTotalHeight = 0;
      newItemKeys.forEach((itemKey) => {
        newTotalHeight += newItemHeights[itemKey] || 0;
      });
      const currentHeight = getContentHeight();
      if (newTotalHeight !== currentHeight) {
        setContentHeight(newTotalHeight);
      }
    }
    useImperativeHandle(
      ref,
      () => ({
        getContentHeight,
        getViewportHeight: () => getViewportSize().height,
        getBottomOffset: () => Math.max(0, getContentHeight() - getViewportSize().height),
        getItemHeight: (index) => {
          const key = itemKeysRef.current[index] || index;
          return itemHeightsRef.current[key] || 0;
        },
        remeasure: measureViewport,
        remeasureItem: (index) => setItemMeasureKeys((prev) => ({
          ...prev,
          [index]: (prev[index] || 0) + 1
        })),
        getItemPosition: (index) => {
          if (index < 0 || index >= itemKeysRef.current.length) {
            return null;
          }
          if (index >= firstInvalidOffsetIndexRef.current) {
            let currentOffset = 0;
            let startIndex = 0;
            if (firstInvalidOffsetIndexRef.current > 0) {
              startIndex = firstInvalidOffsetIndexRef.current;
              const prevIndex = startIndex - 1;
              const prevKey = itemKeysRef.current[prevIndex] || prevIndex;
              const prevHeight = itemHeightsRef.current[prevKey] || 0;
              currentOffset = (itemOffsetsRef.current[prevIndex] ?? 0) + prevHeight;
            }
            for (let i = startIndex; i <= index; i++) {
              itemOffsetsRef.current[i] = currentOffset;
              const key2 = itemKeysRef.current[i] || i;
              const height2 = itemHeightsRef.current[key2] || 0;
              currentOffset += height2;
            }
            firstInvalidOffsetIndexRef.current = index + 1;
          }
          const top = itemOffsetsRef.current[index] ?? 0;
          const key = itemKeysRef.current[index] || index;
          const height = itemHeightsRef.current[key] || 0;
          return { top, height };
        }
      }),
      []
    );
    return /* @__PURE__ */ jsx2(Box2, { ...boxProps, children: /* @__PURE__ */ jsx2(Box2, { ref: viewportRef, width: "100%", children: /* @__PURE__ */ jsx2(Box2, { overflow: debug ? void 0 : "hidden", width: "100%", children: /* @__PURE__ */ jsx2(
      Box2,
      {
        ref: contentRef,
        width: "100%",
        flexDirection: "column",
        marginTop: -scrollOffset,
        children: Children.map(children, (child, index) => {
          if (!child) return null;
          return /* @__PURE__ */ jsx2(
            MeasurableItem,
            {
              index,
              width: viewportSize.width,
              onMeasure: handleItemMeasure,
              measureKey: itemMeasureKeys[index],
              children: child
            },
            isValidElement(child) ? child.key || index : index
          );
        })
      }
    ) }) }) });
  }
);
function useStateRef2(initialValue) {
  const [state, setStateInternal] = useState22(initialValue);
  const ref = useRef2(initialValue);
  const setState = useCallback22((update) => {
    const nextValue = typeof update === "function" ? update(ref.current) : update;
    ref.current = nextValue;
    setStateInternal(nextValue);
  }, []);
  const getState = useCallback22(() => ref.current, []);
  return [state, setState, getState];
}
var ScrollView = forwardRef2(
  ({
    onScroll,
    onViewportSizeChange,
    onContentHeightChange,
    onItemHeightChange,
    debug = false,
    children,
    ...boxProps
  }, ref) => {
    const [scrollOffset, setScrollOffset, getScrollOffset] = useStateRef2(0);
    const innerRef = useRef2(null);
    const contentHeightRef = useRef2(0);
    const handleContentHeightChange = useCallback22(
      (height, previousHeight) => {
        contentHeightRef.current = height;
        onContentHeightChange?.(height, previousHeight);
        if (getScrollOffset() > height) {
          setScrollOffset(height);
          onScroll?.(height);
        }
      },
      [onContentHeightChange, onScroll, getScrollOffset, setScrollOffset]
    );
    const getBottomOffset = useCallback22(
      () => Math.max(
        0,
        contentHeightRef.current - (innerRef.current?.getViewportHeight() || 0)
      ),
      []
    );
    useImperativeHandle2(
      ref,
      () => ({
        scrollTo: (offset) => {
          if (typeof offset !== "number" || isNaN(offset)) {
            return;
          }
          const currentContentHeight = contentHeightRef.current;
          const newScrollTop = Math.max(
            0,
            Math.min(offset, currentContentHeight)
          );
          if (newScrollTop !== getScrollOffset()) {
            setScrollOffset(newScrollTop);
            onScroll?.(newScrollTop);
          }
        },
        scrollBy: (delta) => {
          if (typeof delta !== "number" || isNaN(delta)) {
            return;
          }
          const currentContentHeight = contentHeightRef.current;
          const newScrollTop = Math.max(
            0,
            Math.min(getScrollOffset() + delta, currentContentHeight)
          );
          if (newScrollTop !== getScrollOffset()) {
            setScrollOffset(newScrollTop);
            onScroll?.(newScrollTop);
          }
        },
        scrollToTop: () => {
          if (getScrollOffset() !== 0) {
            setScrollOffset(0);
            onScroll?.(0);
          }
        },
        scrollToBottom: () => {
          const bottomOffset = getBottomOffset();
          if (getScrollOffset() !== bottomOffset) {
            setScrollOffset(bottomOffset);
            onScroll?.(bottomOffset);
          }
        },
        getScrollOffset,
        getContentHeight: () => contentHeightRef.current,
        getViewportHeight: () => innerRef.current?.getViewportHeight() || 0,
        getBottomOffset,
        getItemHeight: (index) => innerRef.current?.getItemHeight(index) || 0,
        getItemPosition: (index) => innerRef.current?.getItemPosition(index) || null,
        remeasure: () => innerRef.current?.remeasure(),
        remeasureItem: (index) => innerRef.current?.remeasureItem(index)
      }),
      [onScroll, getBottomOffset, getScrollOffset, setScrollOffset]
    );
    return /* @__PURE__ */ jsx22(
      ControlledScrollView,
      {
        ref: innerRef,
        scrollOffset,
        onViewportSizeChange,
        onContentHeightChange: handleContentHeightChange,
        onItemHeightChange,
        debug,
        children,
        ...boxProps
      }
    );
  }
);

// src/components/InfoTab.tsx
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
function InfoTab({
  serverName,
  serverConfig,
  serverSettings = null,
  serverState,
  width,
  height,
  focused = false
}) {
  const headerPairs = serverSettings?.headers ?? [];
  const headersBlock = headerPairs.length > 0 ? /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
    "Headers:",
    " ",
    headerPairs.map(({ key, value }) => `${key}=${value}`).join(", ")
  ] }) }) : null;
  const scrollViewRef = useRef3(null);
  useInput(
    (input, key) => {
      if (focused) {
        if (key.upArrow) {
          scrollViewRef.current?.scrollBy(-1);
        } else if (key.downArrow) {
          scrollViewRef.current?.scrollBy(1);
        } else if (key.pageUp) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(-viewportHeight);
        } else if (key.pageDown) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(viewportHeight);
        }
      }
    },
    { isActive: focused }
  );
  return /* @__PURE__ */ jsxs2(Box3, { width, height, flexDirection: "column", paddingX: 1, children: [
    /* @__PURE__ */ jsx3(Box3, { paddingY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx3(Text2, { bold: true, backgroundColor: focused ? "yellow" : void 0, children: "Info" }) }),
    serverName ? /* @__PURE__ */ jsxs2(Fragment, { children: [
      /* @__PURE__ */ jsx3(Box3, { height: height - 4, overflow: "hidden", paddingTop: 1, children: /* @__PURE__ */ jsxs2(ScrollView, { ref: scrollViewRef, height: height - 4, children: [
        /* @__PURE__ */ jsx3(Box3, { flexShrink: 0, marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { bold: true, children: "Server Configuration" }) }),
        serverConfig ? /* @__PURE__ */ jsx3(
          Box3,
          {
            flexShrink: 0,
            marginTop: 1,
            paddingLeft: 2,
            flexDirection: "column",
            children: serverConfig.type === void 0 || serverConfig.type === "stdio" ? /* @__PURE__ */ jsxs2(Fragment, { children: [
              /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "Type: stdio" }),
              /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
                "Command: ",
                serverConfig.command
              ] }),
              serverConfig.args && serverConfig.args.length > 0 && /* @__PURE__ */ jsxs2(Box3, { marginTop: 1, flexDirection: "column", children: [
                /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "Args:" }),
                serverConfig.args.map((arg, idx) => /* @__PURE__ */ jsx3(
                  Box3,
                  {
                    paddingLeft: 2,
                    marginTop: idx === 0 ? 0 : 0,
                    children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: arg })
                  },
                  `arg-${idx}`
                ))
              ] }),
              serverConfig.env && Object.keys(serverConfig.env).length > 0 && /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
                "Env:",
                " ",
                Object.entries(serverConfig.env).map(([k, v]) => `${k}=${v}`).join(", ")
              ] }) }),
              serverConfig.cwd && /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
                "CWD: ",
                serverConfig.cwd
              ] }) })
            ] }) : serverConfig.type === "sse" ? /* @__PURE__ */ jsxs2(Fragment, { children: [
              /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "Type: sse" }),
              /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
                "URL: ",
                serverConfig.url
              ] }),
              headersBlock
            ] }) : serverConfig.type === "streamable-http" ? /* @__PURE__ */ jsxs2(Fragment, { children: [
              /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "Type: streamable-http" }),
              /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
                "URL: ",
                serverConfig.url
              ] }),
              headersBlock
            ] }) : null
          }
        ) : /* @__PURE__ */ jsx3(Box3, { marginTop: 1, paddingLeft: 2, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "No configuration available" }) }),
        serverState && serverState.status === "connected" && serverState.serverInfo && /* @__PURE__ */ jsxs2(Fragment, { children: [
          /* @__PURE__ */ jsx3(Box3, { flexShrink: 0, marginTop: 2, children: /* @__PURE__ */ jsx3(Text2, { bold: true, children: "Server Information" }) }),
          /* @__PURE__ */ jsxs2(
            Box3,
            {
              flexShrink: 0,
              marginTop: 1,
              paddingLeft: 2,
              flexDirection: "column",
              children: [
                serverState.serverInfo.name && /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
                  "Name: ",
                  serverState.serverInfo.name
                ] }),
                serverState.serverInfo.version && /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
                  "Version: ",
                  serverState.serverInfo.version
                ] }) }),
                serverState.instructions && /* @__PURE__ */ jsxs2(Box3, { marginTop: 1, flexDirection: "column", children: [
                  /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "Instructions:" }),
                  /* @__PURE__ */ jsx3(Box3, { paddingLeft: 2, marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: serverState.instructions }) })
                ] })
              ]
            }
          )
        ] }),
        serverState && serverState.status === "error" && /* @__PURE__ */ jsxs2(Box3, { flexShrink: 0, marginTop: 2, children: [
          /* @__PURE__ */ jsx3(Text2, { bold: true, color: "red", children: "Error" }),
          serverState.error && /* @__PURE__ */ jsx3(Box3, { marginTop: 1, paddingLeft: 2, children: /* @__PURE__ */ jsx3(Text2, { color: "red", children: serverState.error }) })
        ] }),
        serverState && serverState.status === "disconnected" && /* @__PURE__ */ jsx3(Box3, { flexShrink: 0, marginTop: 2, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "Server not connected" }) })
      ] }) }),
      focused && /* @__PURE__ */ jsx3(
        Box3,
        {
          flexShrink: 0,
          height: 1,
          justifyContent: "center",
          backgroundColor: "gray",
          children: /* @__PURE__ */ jsx3(Text2, { bold: true, color: "white", children: "\u2191/\u2193 to scroll, + to zoom" })
        }
      )
    ] }) : null
  ] });
}

// src/components/AuthTab.tsx
import { useState as useState10, useEffect as useEffect9, useRef as useRef4, useCallback as useCallback8 } from "react";
import { Box as Box5, Text as Text4, useInput as useInput2 } from "ink";

// src/components/SelectableItem.tsx
import { Box as Box4, Text as Text3 } from "ink";
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
function SelectableItem({
  isSelected,
  bold,
  children
}) {
  return /* @__PURE__ */ jsxs3(Box4, { flexShrink: 0, flexDirection: "row", children: [
    /* @__PURE__ */ jsx4(Box4, { width: 2, children: /* @__PURE__ */ jsx4(Text3, { bold, children: isSelected ? "\u25B6 " : "  " }) }),
    /* @__PURE__ */ jsx4(Text3, { bold, children })
  ] });
}

// src/utils/oauthDisplay.ts
function formatAuthProtocol(protocol) {
  return protocol === "ema" ? "Enterprise-managed" : "Standard";
}
function formatIdpSession(session) {
  switch (session) {
    case "logged_in":
      return "Signed in";
    case "expired":
      return "Session expired";
    default:
      return "Not signed in";
  }
}
function formatClientRegistrationKind(kind) {
  switch (kind) {
    case "static":
      return "Static (preregistered)";
    case "dcr":
      return "Dynamic (DCR)";
    case "cimd":
      return "Client ID Metadata (CIMD)";
  }
}
function formatScopes(state) {
  const scopeSource = state.grantedScope ?? state.configuredScope;
  const scopes = scopeSource?.split(" ").filter(Boolean);
  return scopes && scopes.length > 0 ? scopes.join(", ") : void 0;
}

// src/components/AuthTab.tsx
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
function OAuthDetailRow({ label, value }) {
  return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "row", gap: 1, children: [
    /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
      label,
      ":"
    ] }),
    /* @__PURE__ */ jsx5(Text4, { children: value })
  ] });
}
function AuthTab({
  serverName,
  inspectorClient,
  oauthStatus,
  oauthMessage,
  oauthRevision,
  pendingStepUp,
  onAuthorizeStepUp,
  onCancelStepUp,
  width,
  height,
  focused = false,
  onClearOAuth,
  connectionStatus
}) {
  const isLiveConnection = connectionStatus === "connected" || connectionStatus === "connecting";
  const scrollViewRef = useRef4(null);
  const [oauthState, setOauthState] = useState10(void 0);
  const [clearedConfirmation, setClearedConfirmation] = useState10(false);
  const [lastClearDisconnected, setLastClearDisconnected] = useState10(false);
  const [stepUpChoiceIndex, setStepUpChoiceIndex] = useState10(0);
  const refreshOAuthState = useCallback8(async () => {
    if (!inspectorClient) {
      setOauthState(void 0);
      return;
    }
    const state = await inspectorClient.getOAuthState();
    setOauthState(state);
  }, [inspectorClient]);
  useEffect9(() => {
    void refreshOAuthState();
  }, [refreshOAuthState, oauthRevision, connectionStatus]);
  useEffect9(() => {
    setClearedConfirmation(false);
    setLastClearDisconnected(false);
  }, [oauthRevision]);
  useEffect9(() => {
    setStepUpChoiceIndex(0);
  }, [pendingStepUp]);
  useEffect9(() => {
    if (!inspectorClient) return;
    const update = () => {
      void refreshOAuthState();
    };
    inspectorClient.addEventListener("oauthComplete", update);
    return () => {
      inspectorClient.removeEventListener("oauthComplete", update);
    };
  }, [inspectorClient, refreshOAuthState]);
  useInput2(
    (input, key) => {
      if (!focused) return;
      if (pendingStepUp) {
        if (key.upArrow) {
          setStepUpChoiceIndex((i) => Math.max(0, i - 1));
          return;
        }
        if (key.downArrow) {
          setStepUpChoiceIndex((i) => Math.min(1, i + 1));
          return;
        }
        if (key.return) {
          if (stepUpChoiceIndex === 0) {
            onAuthorizeStepUp?.();
          } else {
            onCancelStepUp?.();
          }
          return;
        }
        if (input.toLowerCase() === "a") {
          onAuthorizeStepUp?.();
          return;
        }
        if (input.toLowerCase() === "c") {
          onCancelStepUp?.();
          return;
        }
        return;
      }
      if (key.upArrow && scrollViewRef.current) {
        scrollViewRef.current.scrollBy(-1);
      } else if (key.downArrow && scrollViewRef.current) {
        scrollViewRef.current.scrollBy(1);
      } else if (key.pageUp && scrollViewRef.current) {
        const h = scrollViewRef.current.getViewportHeight() || 1;
        scrollViewRef.current.scrollBy(-h);
      } else if (key.pageDown && scrollViewRef.current) {
        const h = scrollViewRef.current.getViewportHeight() || 1;
        scrollViewRef.current.scrollBy(h);
      } else if (input.toLowerCase() === "s") {
        setLastClearDisconnected(isLiveConnection);
        onClearOAuth();
        setClearedConfirmation(true);
      }
    },
    { isActive: focused }
  );
  if (!serverName) {
    return /* @__PURE__ */ jsx5(Box5, { width, height, paddingX: 1, paddingY: 1, children: /* @__PURE__ */ jsx5(Text4, { dimColor: true, children: "Select a server to view authentication." }) });
  }
  const scopes = oauthState ? formatScopes(oauthState) : void 0;
  const accessToken = oauthState?.tokens?.access_token;
  return /* @__PURE__ */ jsxs4(Box5, { width, height, flexDirection: "column", paddingX: 1, children: [
    /* @__PURE__ */ jsx5(Box5, { paddingY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx5(Text4, { bold: true, backgroundColor: focused ? "yellow" : void 0, children: "OAuth" }) }),
    /* @__PURE__ */ jsx5(ScrollView, { ref: scrollViewRef, height: height - 8, children: /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 0, children: [
      oauthStatus === "authenticating" && /* @__PURE__ */ jsx5(Text4, { color: "yellow", children: "Authenticating\u2026" }),
      oauthStatus === "error" && oauthMessage && /* @__PURE__ */ jsx5(Text4, { color: "red", children: oauthMessage }),
      oauthStatus === "idle" && oauthMessage && /* @__PURE__ */ jsx5(Text4, { color: "cyan", children: oauthMessage }),
      pendingStepUp ? /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, flexDirection: "column", gap: 0, children: [
        /* @__PURE__ */ jsx5(Text4, { bold: true, color: "yellow", children: stepUpModalTitle({
          enterpriseManaged: pendingStepUp.enterpriseManaged
        }) }),
        /* @__PURE__ */ jsxs4(Text4, { children: [
          stepUpConfirmMessage(pendingStepUp.challenge, {
            enterpriseManaged: pendingStepUp.enterpriseManaged
          }),
          " ",
          stepUpFollowUpMessage({
            enterpriseManaged: pendingStepUp.enterpriseManaged
          })
        ] }),
        (() => {
          const scopes2 = pendingStepUp.authorizationScopes ?? pendingStepUp.challenge.requiredScopes;
          if (!scopes2?.length) return null;
          return /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, flexDirection: "column", children: [
            /* @__PURE__ */ jsx5(Text4, { dimColor: true, children: pendingStepUp.enterpriseManaged ? "Permissions requested:" : "Scopes requested:" }),
            scopes2.map((scope) => /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
              " ",
              "\u2022 ",
              scope
            ] }, scope))
          ] });
        })(),
        /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, flexDirection: "column", children: [
          /* @__PURE__ */ jsx5(SelectableItem, { isSelected: stepUpChoiceIndex === 0, bold: true, children: (() => {
            const label = stepUpAuthorizeActionLabel({
              enterpriseManaged: pendingStepUp.enterpriseManaged
            });
            return /* @__PURE__ */ jsxs4(Fragment2, { children: [
              /* @__PURE__ */ jsx5(Text4, { underline: true, children: label[0] }),
              label.slice(1)
            ] });
          })() }),
          /* @__PURE__ */ jsxs4(SelectableItem, { isSelected: stepUpChoiceIndex === 1, children: [
            /* @__PURE__ */ jsx5(Text4, { underline: true, children: "C" }),
            "ancel"
          ] })
        ] })
      ] }) : null,
      oauthState ? /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", marginTop: 1, gap: 0, children: [
        /* @__PURE__ */ jsx5(Text4, { bold: true, children: "OAuth Details" }),
        /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, flexDirection: "column", paddingLeft: 0, gap: 0, children: [
          /* @__PURE__ */ jsx5(
            OAuthDetailRow,
            {
              label: "Protocol",
              value: formatAuthProtocol(oauthState.protocol)
            }
          ),
          /* @__PURE__ */ jsx5(
            OAuthDetailRow,
            {
              label: "Status",
              value: oauthState.authorized ? "Authorized" : "Not authorized"
            }
          ),
          oauthState.client?.clientId && /* @__PURE__ */ jsx5(
            OAuthDetailRow,
            {
              label: "Client ID",
              value: oauthState.client.clientId
            }
          ),
          oauthState.client?.registrationKind && /* @__PURE__ */ jsx5(
            OAuthDetailRow,
            {
              label: "Client registration",
              value: formatClientRegistrationKind(
                oauthState.client.registrationKind
              )
            }
          ),
          oauthState.protocol === "ema" && oauthState.ema?.idpSession && /* @__PURE__ */ jsx5(
            OAuthDetailRow,
            {
              label: "IdP session",
              value: formatIdpSession(oauthState.ema.idpSession)
            }
          ),
          oauthState.authorizationServerMetadata?.authorization_endpoint && /* @__PURE__ */ jsx5(
            OAuthDetailRow,
            {
              label: "Auth URL",
              value: oauthState.authorizationServerMetadata.authorization_endpoint
            }
          ),
          scopes && /* @__PURE__ */ jsx5(OAuthDetailRow, { label: "Scopes", value: scopes }),
          accessToken && /* @__PURE__ */ jsx5(
            OAuthDetailRow,
            {
              label: "Access token",
              value: `${accessToken.slice(0, 24)}\u2026`
            }
          )
        ] })
      ] }) : oauthStatus !== "authenticating" && /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, flexDirection: "column", gap: 0, children: [
        /* @__PURE__ */ jsx5(Text4, { dimColor: true, children: "No OAuth information yet." }),
        /* @__PURE__ */ jsx5(Text4, { dimColor: true, children: "Connect (C) to authorize when this server requires it." })
      ] }),
      /* @__PURE__ */ jsxs4(Box5, { marginTop: 2, flexDirection: "column", gap: 0, children: [
        /* @__PURE__ */ jsxs4(SelectableItem, { isSelected: true, bold: true, children: [
          "Clear OAuth ",
          /* @__PURE__ */ jsx5(Text4, { underline: true, children: "S" }),
          "tate",
          isLiveConnection && " and disconnect"
        ] }),
        clearedConfirmation && /* @__PURE__ */ jsx5(Text4, { color: "green", children: lastClearDisconnected ? "OAuth state cleared. Disconnected." : "OAuth state cleared." })
      ] })
    ] }) }),
    focused && /* @__PURE__ */ jsx5(
      Box5,
      {
        flexShrink: 0,
        height: 1,
        justifyContent: "center",
        backgroundColor: "gray",
        children: /* @__PURE__ */ jsx5(Text4, { bold: true, color: "white", children: pendingStepUp ? "\u2191/\u2193 select, Enter confirm, A authorize, C cancel" : `S ${isLiveConnection ? "clear+disconnect" : "clear"}, \u2191/\u2193 scroll` })
      }
    )
  ] });
}

// src/components/ResourcesTab.tsx
import { useState as useState12, useEffect as useEffect11, useRef as useRef5, useMemo } from "react";
import { Box as Box6, Text as Text5, useInput as useInput3 } from "ink";

// src/hooks/useSelectableList.ts
import { useState as useState11, useEffect as useEffect10, useCallback as useCallback9 } from "react";
function clampFirstVisible(first, selected, visibleCount) {
  if (selected < first) return selected;
  if (selected >= first + visibleCount) return selected - visibleCount + 1;
  return first;
}
function useSelectableList(itemCount, visibleCount, options) {
  const [selectedIndex, setSelectedIndex] = useState11(0);
  const [firstVisible, setFirstVisible] = useState11(0);
  const setSelection = useCallback9(
    (newIndex) => {
      setSelectedIndex(newIndex);
      setFirstVisible(
        (prev) => clampFirstVisible(prev, newIndex, visibleCount)
      );
    },
    [visibleCount]
  );
  useEffect10(() => {
    if (options?.resetWhen !== void 0) {
      setSelectedIndex(0);
      setFirstVisible(0);
    }
  }, [options?.resetWhen]);
  useEffect10(() => {
    if (itemCount > 0 && selectedIndex >= itemCount) {
      const newIndex = itemCount - 1;
      setSelectedIndex(newIndex);
      setFirstVisible(
        (prev) => clampFirstVisible(prev, newIndex, visibleCount)
      );
    }
  }, [itemCount, selectedIndex, visibleCount]);
  return { selectedIndex, firstVisible, setSelection };
}

// src/components/ResourcesTab.tsx
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
function ResourcesTab({
  resources,
  resourceTemplates = [],
  inspectorClient,
  width,
  height,
  onCountChange,
  focusedPane = null,
  onViewDetails,
  onFetchResource,
  onFetchTemplate,
  onAuthRecoveryRequired,
  modalOpen = false
}) {
  const [error, setError] = useState12(null);
  const [resourceContent, setResourceContent] = useState12(null);
  const [loading, setLoading] = useState12(false);
  const [shouldFetchResource, setShouldFetchResource] = useState12(
    null
  );
  const scrollViewRef = useRef5(null);
  const allItems = useMemo(
    () => [
      ...resources.map((r) => ({ type: "resource", data: r })),
      ...resourceTemplates.map((t) => ({ type: "template", data: t }))
    ],
    [resources, resourceTemplates]
  );
  const totalCount = useMemo(
    () => resources.length + resourceTemplates.length,
    [resources.length, resourceTemplates.length]
  );
  const visibleCount = Math.max(1, height - 7);
  const { selectedIndex, firstVisible, setSelection } = useSelectableList(
    totalCount,
    visibleCount,
    { resetWhen: resources }
  );
  const selectedItem = useMemo(
    () => allItems[selectedIndex] || null,
    [allItems, selectedIndex]
  );
  useInput3(
    (input, key) => {
      if (key.return && selectedItem && inspectorClient && (onFetchResource || onFetchTemplate)) {
        if (selectedItem.type === "resource" && selectedItem.data.uri) {
          setShouldFetchResource(selectedItem.data.uri);
          if (onFetchResource) {
            onFetchResource(selectedItem.data);
          }
        } else if (selectedItem.type === "template" && onFetchTemplate) {
          onFetchTemplate(selectedItem.data);
        }
        return;
      }
      if (focusedPane === "list") {
        if (key.upArrow && selectedIndex > 0) {
          setSelection(selectedIndex - 1);
        } else if (key.downArrow && selectedIndex < totalCount - 1) {
          setSelection(selectedIndex + 1);
        }
        return;
      }
      if (focusedPane === "details") {
        if (input === "+" && resourceContent && onViewDetails) {
          onViewDetails({ content: resourceContent });
          return;
        }
        if (key.upArrow) {
          scrollViewRef.current?.scrollBy(-1);
        } else if (key.downArrow) {
          scrollViewRef.current?.scrollBy(1);
        } else if (key.pageUp) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(-viewportHeight);
        } else if (key.pageDown) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(viewportHeight);
        }
      }
    },
    {
      isActive: !modalOpen && (focusedPane === "list" || focusedPane === "details")
    }
  );
  useEffect11(() => {
    scrollViewRef.current?.scrollTo(0);
  }, [selectedIndex]);
  const prevResourcesRef = useRef5(resources);
  useEffect11(() => {
    if (prevResourcesRef.current !== resources) {
      setResourceContent(null);
      setShouldFetchResource(null);
      prevResourcesRef.current = resources;
    }
  }, [resources]);
  const isResource = selectedItem?.type === "resource";
  const isTemplate = selectedItem?.type === "template";
  const selectedResource = isResource ? selectedItem.data : null;
  const selectedTemplate = isTemplate ? selectedItem.data : null;
  useEffect11(() => {
    if (!shouldFetchResource || !inspectorClient) return;
    const fetchContent = async () => {
      setLoading(true);
      setError(null);
      try {
        const invocation = await inspectorClient.readResource(shouldFetchResource);
        setResourceContent(invocation.result);
      } catch (err) {
        if (err instanceof AuthRecoveryRequiredError) {
          onAuthRecoveryRequired?.(err);
          return;
        }
        setError(
          err instanceof Error ? err.message : "Failed to read resource"
        );
        setResourceContent(null);
      } finally {
        setLoading(false);
        setShouldFetchResource(null);
      }
    };
    fetchContent();
  }, [shouldFetchResource, inspectorClient, onAuthRecoveryRequired]);
  const listWidth = Math.floor(width * 0.4);
  const detailWidth = width - listWidth;
  const prevCountRef = useRef5(totalCount);
  useEffect11(() => {
    if (prevCountRef.current !== totalCount) {
      prevCountRef.current = totalCount;
      onCountChange?.(totalCount);
    }
  }, [totalCount, onCountChange]);
  return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "row", width, height, children: [
    /* @__PURE__ */ jsxs5(
      Box6,
      {
        width: listWidth,
        height,
        borderStyle: "single",
        borderTop: false,
        borderBottom: false,
        borderLeft: false,
        borderRight: true,
        flexDirection: "column",
        paddingX: 1,
        children: [
          /* @__PURE__ */ jsx6(Box6, { paddingY: 1, children: /* @__PURE__ */ jsxs5(
            Text5,
            {
              bold: true,
              backgroundColor: focusedPane === "list" ? "yellow" : void 0,
              children: [
                "Resources (",
                totalCount,
                ")"
              ]
            }
          ) }),
          error ? /* @__PURE__ */ jsx6(Box6, { paddingY: 1, children: /* @__PURE__ */ jsx6(Text5, { color: "red", children: error }) }) : totalCount === 0 ? /* @__PURE__ */ jsx6(Box6, { paddingY: 1, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "No resources available" }) }) : /* @__PURE__ */ jsx6(
            Box6,
            {
              flexDirection: "column",
              height: visibleCount,
              overflow: "hidden",
              flexShrink: 0,
              children: allItems.slice(firstVisible, firstVisible + visibleCount).map((item, i) => {
                const index = firstVisible + i;
                const isSelected = index === selectedIndex;
                const label = item.type === "resource" ? item.data.name || item.data.uri || `Resource ${index + 1}` : item.data.name || `Template ${index - resources.length + 1}`;
                const key = item.type === "resource" ? item.data.uri || index : item.data.uriTemplate || index;
                return /* @__PURE__ */ jsx6(Box6, { paddingY: 0, flexShrink: 0, children: /* @__PURE__ */ jsxs5(Text5, { children: [
                  isSelected ? "\u25B6 " : "  ",
                  label
                ] }) }, key);
              })
            }
          )
        ]
      }
    ),
    /* @__PURE__ */ jsx6(
      Box6,
      {
        width: detailWidth,
        height,
        paddingX: 1,
        flexDirection: "column",
        overflow: "hidden",
        children: selectedResource ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
          /* @__PURE__ */ jsx6(Box6, { flexShrink: 0, paddingTop: 1, children: /* @__PURE__ */ jsx6(
            Text5,
            {
              bold: true,
              backgroundColor: focusedPane === "details" ? "yellow" : void 0,
              ...focusedPane === "details" ? {} : { color: "cyan" },
              children: selectedResource.name || selectedResource.uri
            }
          ) }),
          /* @__PURE__ */ jsxs5(ScrollView, { ref: scrollViewRef, height: height - 3, children: [
            selectedResource.description && /* @__PURE__ */ jsx6(Fragment3, { children: selectedResource.description.split("\n").map((line, idx) => /* @__PURE__ */ jsx6(
              Box6,
              {
                marginTop: idx === 0 ? 1 : 0,
                flexShrink: 0,
                children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: line })
              },
              `desc-${idx}`
            )) }),
            selectedResource.uri && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
              "URI: ",
              selectedResource.uri
            ] }) }),
            selectedResource.mimeType && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
              "MIME Type: ",
              selectedResource.mimeType
            ] }) }),
            loading && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx6(Text5, { color: "yellow", children: "Loading resource content..." }) }),
            !loading && resourceContent && /* @__PURE__ */ jsxs5(Fragment3, { children: [
              /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx6(Text5, { bold: true, children: "Content:" }) }),
              /* @__PURE__ */ jsx6(Box6, { marginTop: 1, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: JSON.stringify(resourceContent, null, 2) }) })
            ] }),
            !loading && !resourceContent && selectedResource.uri && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "[Enter to Fetch Resource]" }) })
          ] }),
          focusedPane === "details" && /* @__PURE__ */ jsx6(
            Box6,
            {
              flexShrink: 0,
              height: 1,
              justifyContent: "center",
              backgroundColor: "gray",
              children: /* @__PURE__ */ jsx6(Text5, { bold: true, color: "white", children: resourceContent ? "\u2191/\u2193 to scroll, + to zoom" : "Enter to fetch, \u2191/\u2193 to scroll" })
            }
          )
        ] }) : selectedTemplate ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
          /* @__PURE__ */ jsx6(Box6, { flexShrink: 0, paddingTop: 1, children: /* @__PURE__ */ jsx6(
            Text5,
            {
              bold: true,
              backgroundColor: focusedPane === "details" ? "yellow" : void 0,
              ...focusedPane === "details" ? {} : { color: "cyan" },
              children: selectedTemplate.name
            }
          ) }),
          /* @__PURE__ */ jsxs5(ScrollView, { ref: scrollViewRef, height: height - 3, children: [
            selectedTemplate.description && /* @__PURE__ */ jsx6(Fragment3, { children: selectedTemplate.description.split("\n").map((line, idx) => /* @__PURE__ */ jsx6(
              Box6,
              {
                marginTop: idx === 0 ? 1 : 0,
                flexShrink: 0,
                children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: line })
              },
              `desc-${idx}`
            )) }),
            selectedTemplate.uriTemplate && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
              "URI Template: ",
              selectedTemplate.uriTemplate
            ] }) }),
            /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "[Enter to Fetch Resource]" }) })
          ] }),
          focusedPane === "details" && /* @__PURE__ */ jsx6(
            Box6,
            {
              flexShrink: 0,
              height: 1,
              justifyContent: "center",
              backgroundColor: "gray",
              children: /* @__PURE__ */ jsx6(Text5, { bold: true, color: "white", children: "Enter to fetch" })
            }
          )
        ] }) : /* @__PURE__ */ jsx6(Box6, { paddingY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "Select a resource or template to view details" }) })
      }
    )
  ] });
}

// src/components/PromptsTab.tsx
import { useState as useState13, useEffect as useEffect12, useRef as useRef6 } from "react";
import { Box as Box7, Text as Text6, useInput as useInput4 } from "ink";
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
function PromptsTab({
  prompts,
  inspectorClient,
  width,
  height,
  focusedPane = null,
  onViewDetails,
  onFetchPrompt,
  onAuthRecoveryRequired,
  modalOpen = false
}) {
  const visibleCount = Math.max(1, height - 7);
  const { selectedIndex, firstVisible, setSelection } = useSelectableList(
    prompts.length,
    visibleCount,
    { resetWhen: prompts }
  );
  const [error, setError] = useState13(null);
  const scrollViewRef = useRef6(null);
  useInput4(
    (input, key) => {
      if (key.return && selectedPrompt && inspectorClient && onFetchPrompt) {
        if (selectedPrompt.arguments && selectedPrompt.arguments.length > 0) {
          onFetchPrompt(selectedPrompt);
        } else {
          (async () => {
            try {
              const invocation = await inspectorClient.getPrompt(
                selectedPrompt.name
              );
              if (onViewDetails) {
                onViewDetails({
                  ...selectedPrompt,
                  result: invocation.result
                });
              }
            } catch (error2) {
              if (error2 instanceof AuthRecoveryRequiredError) {
                onAuthRecoveryRequired?.(error2);
                return;
              }
              setError(
                error2 instanceof Error ? error2.message : "Failed to get prompt"
              );
            }
          })();
        }
        return;
      }
      if (focusedPane === "list") {
        if (key.upArrow && selectedIndex > 0) {
          setSelection(selectedIndex - 1);
        } else if (key.downArrow && selectedIndex < prompts.length - 1) {
          setSelection(selectedIndex + 1);
        }
        return;
      }
      if (focusedPane === "details") {
        if (input === "+" && selectedPrompt && onViewDetails) {
          onViewDetails(selectedPrompt);
          return;
        }
        if (key.upArrow) {
          scrollViewRef.current?.scrollBy(-1);
        } else if (key.downArrow) {
          scrollViewRef.current?.scrollBy(1);
        } else if (key.pageUp) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(-viewportHeight);
        } else if (key.pageDown) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(viewportHeight);
        }
      }
    },
    {
      isActive: !modalOpen && (focusedPane === "list" || focusedPane === "details")
    }
  );
  useEffect12(() => {
    scrollViewRef.current?.scrollTo(0);
  }, [selectedIndex]);
  const selectedPrompt = prompts[selectedIndex] || null;
  const listWidth = Math.floor(width * 0.4);
  const detailWidth = width - listWidth;
  return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", width, height, children: [
    /* @__PURE__ */ jsxs6(
      Box7,
      {
        width: listWidth,
        height,
        borderStyle: "single",
        borderTop: false,
        borderBottom: false,
        borderLeft: false,
        borderRight: true,
        flexDirection: "column",
        paddingX: 1,
        children: [
          /* @__PURE__ */ jsx7(Box7, { paddingY: 1, children: /* @__PURE__ */ jsxs6(
            Text6,
            {
              bold: true,
              backgroundColor: focusedPane === "list" ? "yellow" : void 0,
              children: [
                "Prompts (",
                prompts.length,
                ")"
              ]
            }
          ) }),
          error ? /* @__PURE__ */ jsx7(Box7, { paddingY: 1, children: /* @__PURE__ */ jsx7(Text6, { color: "red", children: error }) }) : prompts.length === 0 ? /* @__PURE__ */ jsx7(Box7, { paddingY: 1, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "No prompts available" }) }) : /* @__PURE__ */ jsx7(
            Box7,
            {
              flexDirection: "column",
              height: visibleCount,
              overflow: "hidden",
              flexShrink: 0,
              children: prompts.slice(firstVisible, firstVisible + visibleCount).map((prompt, i) => {
                const index = firstVisible + i;
                const isSelected = index === selectedIndex;
                return /* @__PURE__ */ jsx7(Box7, { paddingY: 0, flexShrink: 0, children: /* @__PURE__ */ jsxs6(Text6, { children: [
                  isSelected ? "\u25B6 " : "  ",
                  prompt.name || `Prompt ${index + 1}`
                ] }) }, prompt.name || index);
              })
            }
          )
        ]
      }
    ),
    /* @__PURE__ */ jsx7(
      Box7,
      {
        width: detailWidth,
        height,
        paddingX: 1,
        flexDirection: "column",
        overflow: "hidden",
        children: selectedPrompt ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
          /* @__PURE__ */ jsx7(Box7, { flexShrink: 0, paddingTop: 1, children: /* @__PURE__ */ jsx7(
            Text6,
            {
              bold: true,
              backgroundColor: focusedPane === "details" ? "yellow" : void 0,
              ...focusedPane === "details" ? {} : { color: "cyan" },
              children: selectedPrompt.name
            }
          ) }),
          /* @__PURE__ */ jsxs6(ScrollView, { ref: scrollViewRef, height: height - 5, children: [
            selectedPrompt.description && /* @__PURE__ */ jsx7(Fragment4, { children: selectedPrompt.description.split("\n").map((line, idx) => /* @__PURE__ */ jsx7(
              Box7,
              {
                marginTop: idx === 0 ? 1 : 0,
                flexShrink: 0,
                children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: line })
              },
              `desc-${idx}`
            )) }),
            selectedPrompt.arguments && selectedPrompt.arguments.length > 0 && /* @__PURE__ */ jsxs6(Fragment4, { children: [
              /* @__PURE__ */ jsx7(Box7, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text6, { bold: true, children: "Arguments:" }) }),
              selectedPrompt.arguments.map(
                (arg, idx) => /* @__PURE__ */ jsx7(
                  Box7,
                  {
                    marginTop: 1,
                    paddingLeft: 2,
                    flexShrink: 0,
                    children: /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
                      "- ",
                      arg.name,
                      ":",
                      " ",
                      arg.description ?? arg.type ?? "string"
                    ] })
                  },
                  `arg-${idx}`
                )
              )
            ] }),
            /* @__PURE__ */ jsx7(Box7, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "[Enter to Get Prompt]" }) })
          ] }),
          focusedPane === "details" && /* @__PURE__ */ jsx7(
            Box7,
            {
              flexShrink: 0,
              height: 1,
              justifyContent: "center",
              backgroundColor: "gray",
              children: /* @__PURE__ */ jsx7(Text6, { bold: true, color: "white", children: "\u2191/\u2193 to scroll, + to zoom" })
            }
          )
        ] }) : /* @__PURE__ */ jsx7(Box7, { paddingY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "Select a prompt to view details" }) })
      }
    )
  ] });
}

// src/components/ToolsTab.tsx
import { useState as useState14, useEffect as useEffect13, useRef as useRef7 } from "react";
import { Box as Box8, Text as Text7, useInput as useInput5 } from "ink";
import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
function ToolsTab({
  tools,
  isConnected,
  width,
  height,
  focusedPane = null,
  onTestTool,
  onViewDetails,
  modalOpen = false
}) {
  const visibleCount = Math.max(1, height - 7);
  const { selectedIndex, firstVisible, setSelection } = useSelectableList(
    tools.length,
    visibleCount,
    { resetWhen: tools }
  );
  const [error] = useState14(null);
  const scrollViewRef = useRef7(null);
  const listWidth = Math.floor(width * 0.4);
  const detailWidth = width - listWidth;
  useInput5(
    (input, key) => {
      if (key.return && selectedTool && isConnected && onTestTool) {
        onTestTool(selectedTool);
        return;
      }
      if (focusedPane === "list") {
        if (key.upArrow && selectedIndex > 0) {
          setSelection(selectedIndex - 1);
        } else if (key.downArrow && selectedIndex < tools.length - 1) {
          setSelection(selectedIndex + 1);
        }
        return;
      }
      if (focusedPane === "details") {
        if (input === "+" && selectedTool && onViewDetails) {
          onViewDetails(selectedTool);
          return;
        }
        if (key.upArrow) {
          scrollViewRef.current?.scrollBy(-1);
        } else if (key.downArrow) {
          scrollViewRef.current?.scrollBy(1);
        } else if (key.pageUp) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(-viewportHeight);
        } else if (key.pageDown) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(viewportHeight);
        }
      }
    },
    {
      isActive: !modalOpen && (focusedPane === "list" || focusedPane === "details")
    }
  );
  useEffect13(() => {
    scrollViewRef.current?.scrollTo(0);
  }, [selectedIndex]);
  const selectedTool = tools[selectedIndex] || null;
  return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", width, height, children: [
    /* @__PURE__ */ jsxs7(
      Box8,
      {
        width: listWidth,
        height,
        borderStyle: "single",
        borderTop: false,
        borderBottom: false,
        borderLeft: false,
        borderRight: true,
        flexDirection: "column",
        paddingX: 1,
        children: [
          /* @__PURE__ */ jsx8(Box8, { paddingY: 1, children: /* @__PURE__ */ jsxs7(
            Text7,
            {
              bold: true,
              backgroundColor: focusedPane === "list" ? "yellow" : void 0,
              children: [
                "Tools (",
                tools.length,
                ")"
              ]
            }
          ) }),
          error ? /* @__PURE__ */ jsx8(Box8, { paddingY: 1, children: /* @__PURE__ */ jsx8(Text7, { color: "red", children: error }) }) : tools.length === 0 ? /* @__PURE__ */ jsx8(Box8, { paddingY: 1, children: /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: "No tools available" }) }) : /* @__PURE__ */ jsx8(
            Box8,
            {
              flexDirection: "column",
              height: visibleCount,
              overflow: "hidden",
              flexShrink: 0,
              children: tools.slice(firstVisible, firstVisible + visibleCount).map((tool, i) => {
                const index = firstVisible + i;
                const isSelected = index === selectedIndex;
                return /* @__PURE__ */ jsx8(Box8, { paddingY: 0, flexShrink: 0, children: /* @__PURE__ */ jsxs7(Text7, { children: [
                  isSelected ? "\u25B6 " : "  ",
                  tool.name || `Tool ${index + 1}`
                ] }) }, tool.name || index);
              })
            }
          )
        ]
      }
    ),
    /* @__PURE__ */ jsx8(
      Box8,
      {
        width: detailWidth,
        height,
        paddingX: 1,
        flexDirection: "column",
        overflow: "hidden",
        children: selectedTool ? /* @__PURE__ */ jsxs7(Fragment5, { children: [
          /* @__PURE__ */ jsxs7(
            Box8,
            {
              flexShrink: 0,
              flexDirection: "row",
              justifyContent: "space-between",
              paddingTop: 1,
              children: [
                /* @__PURE__ */ jsx8(
                  Text7,
                  {
                    bold: true,
                    backgroundColor: focusedPane === "details" ? "yellow" : void 0,
                    ...focusedPane === "details" ? {} : { color: "cyan" },
                    children: selectedTool.name
                  }
                ),
                isConnected && /* @__PURE__ */ jsx8(Text7, { children: /* @__PURE__ */ jsx8(Text7, { color: "cyan", bold: true, children: "[Enter to Test]" }) })
              ]
            }
          ),
          /* @__PURE__ */ jsxs7(ScrollView, { ref: scrollViewRef, height: height - 5, children: [
            selectedTool.description && /* @__PURE__ */ jsx8(Fragment5, { children: selectedTool.description.split("\n").map((line, idx) => /* @__PURE__ */ jsx8(
              Box8,
              {
                marginTop: idx === 0 ? 1 : 0,
                flexShrink: 0,
                children: /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: line })
              },
              `desc-${idx}`
            )) }),
            selectedTool.inputSchema && /* @__PURE__ */ jsxs7(Fragment5, { children: [
              /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx8(Text7, { bold: true, children: "Input Schema:" }) }),
              JSON.stringify(selectedTool.inputSchema, null, 2).split("\n").map((line, idx) => /* @__PURE__ */ jsx8(
                Box8,
                {
                  marginTop: idx === 0 ? 1 : 0,
                  paddingLeft: 2,
                  flexShrink: 0,
                  children: /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: line })
                },
                `schema-${idx}`
              ))
            ] })
          ] }),
          focusedPane === "details" && /* @__PURE__ */ jsx8(
            Box8,
            {
              flexShrink: 0,
              height: 1,
              justifyContent: "center",
              backgroundColor: "gray",
              children: /* @__PURE__ */ jsx8(Text7, { bold: true, color: "white", children: "\u2191/\u2193 to scroll, + to zoom" })
            }
          )
        ] }) : /* @__PURE__ */ jsx8(Box8, { paddingY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: "Select a tool to view details" }) })
      }
    )
  ] });
}

// src/components/NotificationsTab.tsx
import { useEffect as useEffect14, useRef as useRef8 } from "react";
import { Box as Box9, Text as Text8, useInput as useInput6 } from "ink";
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
function NotificationsTab({
  stderrLogs,
  width,
  height,
  onCountChange,
  focused = false
}) {
  const scrollViewRef = useRef8(null);
  const onCountChangeRef = useRef8(onCountChange);
  useEffect14(() => {
    onCountChangeRef.current = onCountChange;
  }, [onCountChange]);
  useEffect14(() => {
    onCountChangeRef.current?.(stderrLogs.length);
  }, [stderrLogs.length]);
  useInput6(
    (input, key) => {
      if (focused) {
        if (key.upArrow) {
          scrollViewRef.current?.scrollBy(-1);
        } else if (key.downArrow) {
          scrollViewRef.current?.scrollBy(1);
        } else if (key.pageUp) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(-viewportHeight);
        } else if (key.pageDown) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(viewportHeight);
        }
      }
    },
    { isActive: focused }
  );
  return /* @__PURE__ */ jsxs8(Box9, { width, height, flexDirection: "column", paddingX: 1, children: [
    /* @__PURE__ */ jsx9(Box9, { paddingY: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs8(Text8, { bold: true, backgroundColor: focused ? "yellow" : void 0, children: [
      "Console (",
      stderrLogs.length,
      ")"
    ] }) }),
    stderrLogs.length === 0 ? /* @__PURE__ */ jsx9(Box9, { paddingY: 1, children: /* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "No stderr output yet" }) }) : /* @__PURE__ */ jsx9(ScrollView, { ref: scrollViewRef, height: height - 3, children: stderrLogs.map((log, index) => /* @__PURE__ */ jsxs8(
      Box9,
      {
        paddingY: 0,
        flexDirection: "row",
        flexShrink: 0,
        children: [
          /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
            "[",
            log.timestamp.toLocaleTimeString(),
            "] "
          ] }),
          /* @__PURE__ */ jsx9(Text8, { color: "red", children: log.message })
        ]
      },
      `log-${log.timestamp.getTime()}-${index}`
    )) })
  ] });
}

// src/components/HistoryTab.tsx
import React7, { useEffect as useEffect15, useRef as useRef9 } from "react";
import { Box as Box10, Text as Text9, useInput as useInput7 } from "ink";
import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
function HistoryTab({
  messages,
  width,
  height,
  onCountChange,
  focusedPane = null,
  onViewDetails,
  modalOpen = false
}) {
  const visibleCount = Math.max(1, height - 7);
  const { selectedIndex, firstVisible, setSelection } = useSelectableList(
    messages.length,
    visibleCount
  );
  const scrollViewRef = useRef9(null);
  const selectedMessage = messages[selectedIndex] || null;
  useInput7(
    (input, key) => {
      if (focusedPane === "messages") {
        if (key.upArrow && selectedIndex > 0) {
          setSelection(selectedIndex - 1);
        } else if (key.downArrow && selectedIndex < messages.length - 1) {
          setSelection(selectedIndex + 1);
        } else if (key.pageUp) {
          setSelection(Math.max(0, selectedIndex - visibleCount));
        } else if (key.pageDown) {
          setSelection(
            Math.min(messages.length - 1, selectedIndex + visibleCount)
          );
        }
        return;
      }
      if (focusedPane === "details") {
        if (input === "+" && selectedMessage && onViewDetails) {
          onViewDetails(selectedMessage);
          return;
        }
        if (key.upArrow) {
          scrollViewRef.current?.scrollBy(-1);
        } else if (key.downArrow) {
          scrollViewRef.current?.scrollBy(1);
        } else if (key.pageUp) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(-viewportHeight);
        } else if (key.pageDown) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(viewportHeight);
        }
      }
    },
    { isActive: !modalOpen && focusedPane !== void 0 }
  );
  React7.useEffect(() => {
    onCountChange?.(messages.length);
  }, [messages.length]);
  useEffect15(() => {
    scrollViewRef.current?.scrollTo(0);
  }, [selectedIndex]);
  const listWidth = Math.floor(width * 0.4);
  const detailWidth = width - listWidth;
  return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", width, height, children: [
    /* @__PURE__ */ jsxs9(
      Box10,
      {
        width: listWidth,
        height,
        borderStyle: "single",
        borderTop: false,
        borderBottom: false,
        borderLeft: false,
        borderRight: true,
        flexDirection: "column",
        paddingX: 1,
        children: [
          /* @__PURE__ */ jsx10(Box10, { paddingY: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs9(
            Text9,
            {
              bold: true,
              backgroundColor: focusedPane === "messages" ? "yellow" : void 0,
              children: [
                "Protocol (",
                messages.length,
                ")"
              ]
            }
          ) }),
          messages.length === 0 ? /* @__PURE__ */ jsx10(Box10, { paddingY: 1, children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "No messages" }) }) : /* @__PURE__ */ jsx10(
            Box10,
            {
              flexDirection: "column",
              height: visibleCount,
              overflow: "hidden",
              flexShrink: 0,
              children: messages.slice(firstVisible, firstVisible + visibleCount).map((msg, i) => {
                const index = firstVisible + i;
                const isSelected = index === selectedIndex;
                let label;
                if (msg.direction === "request" && "method" in msg.message) {
                  label = msg.message.method;
                } else if (msg.direction === "response") {
                  if ("result" in msg.message) {
                    label = "Response (result)";
                  } else if ("error" in msg.message) {
                    label = `Response (error: ${msg.message.error.code})`;
                  } else {
                    label = "Response";
                  }
                } else if (msg.direction === "notification" && "method" in msg.message) {
                  label = msg.message.method;
                } else {
                  label = "Unknown";
                }
                const direction = msg.direction === "request" ? "\u2192" : msg.direction === "response" ? "\u2190" : "\u2022";
                const hasResponse = msg.response !== void 0;
                return /* @__PURE__ */ jsx10(Box10, { paddingY: 0, flexShrink: 0, children: /* @__PURE__ */ jsxs9(Text9, { color: isSelected ? "white" : "white", children: [
                  isSelected ? "\u25B6 " : "  ",
                  direction,
                  " ",
                  label,
                  hasResponse ? " \u2713" : msg.direction === "request" ? " ..." : ""
                ] }) }, msg.id);
              })
            }
          )
        ]
      }
    ),
    /* @__PURE__ */ jsx10(
      Box10,
      {
        width: detailWidth,
        height,
        paddingX: 1,
        flexDirection: "column",
        flexShrink: 0,
        borderStyle: "single",
        borderTop: false,
        borderBottom: false,
        borderLeft: false,
        borderRight: false,
        children: selectedMessage ? /* @__PURE__ */ jsxs9(Fragment6, { children: [
          /* @__PURE__ */ jsx10(Box10, { flexShrink: 0, paddingTop: 1, children: /* @__PURE__ */ jsx10(
            Text9,
            {
              bold: true,
              backgroundColor: focusedPane === "details" ? "yellow" : void 0,
              ...focusedPane === "details" ? {} : { color: "cyan" },
              children: selectedMessage.direction === "request" && "method" in selectedMessage.message ? selectedMessage.message.method : selectedMessage.direction === "response" ? "Response" : selectedMessage.direction === "notification" && "method" in selectedMessage.message ? selectedMessage.message.method : "Message"
            }
          ) }),
          /* @__PURE__ */ jsxs9(ScrollView, { ref: scrollViewRef, height: height - 5, children: [
            /* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexDirection: "column", flexShrink: 0, children: [
              /* @__PURE__ */ jsxs9(Text9, { bold: true, children: [
                "Direction: ",
                selectedMessage.direction
              ] }),
              /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
                selectedMessage.timestamp.toLocaleTimeString(),
                selectedMessage.duration !== void 0 && ` (${selectedMessage.duration}ms)`
              ] }) })
            ] }),
            selectedMessage.direction === "request" ? /* @__PURE__ */ jsxs9(Fragment6, { children: [
              /* @__PURE__ */ jsx10(Box10, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text9, { bold: true, children: "Request:" }) }),
              JSON.stringify(selectedMessage.message, null, 2).split("\n").map((line, idx) => /* @__PURE__ */ jsx10(
                Box10,
                {
                  marginTop: idx === 0 ? 1 : 0,
                  paddingLeft: 2,
                  flexShrink: 0,
                  children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: line })
                },
                `req-${idx}`
              )),
              selectedMessage.response ? /* @__PURE__ */ jsxs9(Fragment6, { children: [
                /* @__PURE__ */ jsx10(Box10, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text9, { bold: true, children: "Response:" }) }),
                JSON.stringify(selectedMessage.response, null, 2).split("\n").map((line, idx) => /* @__PURE__ */ jsx10(
                  Box10,
                  {
                    marginTop: idx === 0 ? 1 : 0,
                    paddingLeft: 2,
                    flexShrink: 0,
                    children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: line })
                  },
                  `resp-${idx}`
                ))
              ] }) : /* @__PURE__ */ jsx10(Box10, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, italic: true, children: "Waiting for response..." }) })
            ] }) : /* @__PURE__ */ jsxs9(Fragment6, { children: [
              /* @__PURE__ */ jsx10(Box10, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text9, { bold: true, children: selectedMessage.direction === "response" ? "Response:" : "Notification:" }) }),
              JSON.stringify(selectedMessage.message, null, 2).split("\n").map((line, idx) => /* @__PURE__ */ jsx10(
                Box10,
                {
                  marginTop: idx === 0 ? 1 : 0,
                  paddingLeft: 2,
                  flexShrink: 0,
                  children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: line })
                },
                `msg-${idx}`
              ))
            ] })
          ] }),
          focusedPane === "details" && /* @__PURE__ */ jsx10(
            Box10,
            {
              flexShrink: 0,
              height: 1,
              justifyContent: "center",
              backgroundColor: "gray",
              children: /* @__PURE__ */ jsx10(Text9, { bold: true, color: "white", children: "\u2191/\u2193 to scroll, + to zoom" })
            }
          )
        ] }) : /* @__PURE__ */ jsx10(Box10, { paddingY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "Select a message to view details" }) })
      }
    )
  ] });
}

// src/components/RequestsTab.tsx
import React8, { useEffect as useEffect16, useRef as useRef10 } from "react";
import { Box as Box11, Text as Text10, useInput as useInput8 } from "ink";
import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
function RequestsTab({
  requests,
  width,
  height,
  onCountChange,
  focusedPane = null,
  onViewDetails,
  modalOpen = false
}) {
  const visibleCount = Math.max(1, height - 7);
  const { selectedIndex, firstVisible, setSelection } = useSelectableList(
    requests.length,
    visibleCount
  );
  const scrollViewRef = useRef10(null);
  const selectedRequest = requests[selectedIndex] || null;
  useInput8(
    (input, key) => {
      if (focusedPane === "requests") {
        if (key.upArrow && selectedIndex > 0) {
          setSelection(selectedIndex - 1);
        } else if (key.downArrow && selectedIndex < requests.length - 1) {
          setSelection(selectedIndex + 1);
        } else if (key.pageUp) {
          setSelection(Math.max(0, selectedIndex - visibleCount));
        } else if (key.pageDown) {
          setSelection(
            Math.min(requests.length - 1, selectedIndex + visibleCount)
          );
        }
        return;
      }
      if (focusedPane === "details") {
        if (input === "+" && selectedRequest && onViewDetails) {
          onViewDetails(selectedRequest);
          return;
        }
        if (key.upArrow) {
          scrollViewRef.current?.scrollBy(-1);
        } else if (key.downArrow) {
          scrollViewRef.current?.scrollBy(1);
        } else if (key.pageUp) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(-viewportHeight);
        } else if (key.pageDown) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(viewportHeight);
        }
      }
    },
    { isActive: !modalOpen && focusedPane !== void 0 }
  );
  React8.useEffect(() => {
    onCountChange?.(requests.length);
  }, [requests.length]);
  useEffect16(() => {
    scrollViewRef.current?.scrollTo(0);
  }, [selectedIndex]);
  const listWidth = Math.floor(width * 0.4);
  const detailWidth = width - listWidth;
  const getStatusColor = (status) => {
    if (!status) return "gray";
    if (status >= 200 && status < 300) return "green";
    if (status >= 300 && status < 400) return "yellow";
    if (status >= 400) return "red";
    return "gray";
  };
  return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", width, height, children: [
    /* @__PURE__ */ jsxs10(
      Box11,
      {
        width: listWidth,
        height,
        borderStyle: "single",
        borderTop: false,
        borderBottom: false,
        borderLeft: false,
        borderRight: true,
        flexDirection: "column",
        paddingX: 1,
        children: [
          /* @__PURE__ */ jsx11(Box11, { paddingY: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs10(
            Text10,
            {
              bold: true,
              backgroundColor: focusedPane === "requests" ? "yellow" : void 0,
              children: [
                "Network (",
                requests.length,
                ")"
              ]
            }
          ) }),
          requests.length === 0 ? /* @__PURE__ */ jsx11(Box11, { paddingY: 1, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: "No requests" }) }) : /* @__PURE__ */ jsx11(
            Box11,
            {
              flexDirection: "column",
              height: visibleCount,
              overflow: "hidden",
              flexShrink: 0,
              children: requests.slice(firstVisible, firstVisible + visibleCount).map((req, i) => {
                const index = firstVisible + i;
                const isSelected = index === selectedIndex;
                const statusColor = getStatusColor(req.responseStatus);
                const statusText = req.responseStatus ? `${req.responseStatus}` : req.error ? "ERROR" : "...";
                const categoryLabel = req.category === "auth" ? "AUTH" : "MCP ";
                const methodPadded = req.method === "GET" ? "GET " : req.method;
                return /* @__PURE__ */ jsx11(Box11, { paddingY: 0, flexShrink: 0, children: /* @__PURE__ */ jsxs10(Text10, { color: isSelected ? "white" : "white", children: [
                  isSelected ? "\u25B6 " : "  ",
                  /* @__PURE__ */ jsx11(Text10, { children: categoryLabel }),
                  " ",
                  /* @__PURE__ */ jsx11(Text10, { color: statusColor, children: methodPadded }),
                  " ",
                  /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: statusText }),
                  req.duration !== void 0 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
                    " ",
                    req.duration,
                    "ms"
                  ] })
                ] }) }, req.id);
              })
            }
          )
        ]
      }
    ),
    /* @__PURE__ */ jsx11(
      Box11,
      {
        width: detailWidth,
        height,
        paddingX: 1,
        flexDirection: "column",
        flexShrink: 0,
        borderStyle: "single",
        borderTop: false,
        borderBottom: false,
        borderLeft: false,
        borderRight: false,
        children: selectedRequest ? /* @__PURE__ */ jsxs10(Fragment7, { children: [
          /* @__PURE__ */ jsx11(Box11, { flexShrink: 0, paddingTop: 1, children: /* @__PURE__ */ jsxs10(
            Text10,
            {
              bold: true,
              backgroundColor: focusedPane === "details" ? "yellow" : void 0,
              ...focusedPane === "details" ? {} : { color: "cyan" },
              children: [
                selectedRequest.method,
                " ",
                selectedRequest.url
              ]
            }
          ) }),
          /* @__PURE__ */ jsxs10(ScrollView, { ref: scrollViewRef, height: height - 5, children: [
            /* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs10(Text10, { bold: true, children: [
              "Category:",
              " ",
              /* @__PURE__ */ jsx11(Text10, { children: selectedRequest.category === "auth" ? "auth" : "transport" })
            ] }) }),
            selectedRequest.responseStatus !== void 0 ? /* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs10(Text10, { bold: true, children: [
              "Status:",
              " ",
              /* @__PURE__ */ jsxs10(
                Text10,
                {
                  color: getStatusColor(selectedRequest.responseStatus),
                  children: [
                    selectedRequest.responseStatus,
                    " ",
                    selectedRequest.responseStatusText || ""
                  ]
                }
              )
            ] }) }) : selectedRequest.error ? /* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs10(Text10, { bold: true, color: "red", children: [
              "Error: ",
              selectedRequest.error
            ] }) }) : /* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, italic: true, children: "Request in progress..." }) }),
            selectedRequest.duration !== void 0 && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
              selectedRequest.timestamp.toLocaleTimeString(),
              " (",
              selectedRequest.duration,
              "ms)"
            ] }) }),
            /* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text10, { bold: true, children: "Request Headers:" }) }),
            Object.entries(selectedRequest.requestHeaders).map(
              ([key, value]) => /* @__PURE__ */ jsx11(Box11, { marginTop: 0, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
                key,
                ": ",
                value
              ] }) }, key)
            ),
            selectedRequest.requestBody && /* @__PURE__ */ jsxs10(Fragment7, { children: [
              /* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text10, { bold: true, children: "Request Body:" }) }),
              (() => {
                try {
                  const parsed = JSON.parse(selectedRequest.requestBody);
                  return JSON.stringify(parsed, null, 2).split("\n").map((line, idx) => /* @__PURE__ */ jsx11(
                    Box11,
                    {
                      marginTop: idx === 0 ? 1 : 0,
                      paddingLeft: 2,
                      flexShrink: 0,
                      children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: line })
                    },
                    `req-body-${idx}`
                  ));
                } catch {
                  return /* @__PURE__ */ jsx11(Box11, { marginTop: 1, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: selectedRequest.requestBody }) });
                }
              })()
            ] }),
            selectedRequest.responseHeaders && Object.keys(selectedRequest.responseHeaders).length > 0 && /* @__PURE__ */ jsxs10(Fragment7, { children: [
              /* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text10, { bold: true, children: "Response Headers:" }) }),
              Object.entries(selectedRequest.responseHeaders).map(
                ([key, value]) => /* @__PURE__ */ jsx11(
                  Box11,
                  {
                    marginTop: 0,
                    paddingLeft: 2,
                    flexShrink: 0,
                    children: /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
                      key,
                      ": ",
                      value
                    ] })
                  },
                  key
                )
              )
            ] }),
            selectedRequest.responseBody && /* @__PURE__ */ jsxs10(Fragment7, { children: [
              /* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text10, { bold: true, children: "Response Body:" }) }),
              (() => {
                try {
                  const parsed = JSON.parse(selectedRequest.responseBody);
                  return JSON.stringify(parsed, null, 2).split("\n").map((line, idx) => /* @__PURE__ */ jsx11(
                    Box11,
                    {
                      marginTop: idx === 0 ? 1 : 0,
                      paddingLeft: 2,
                      flexShrink: 0,
                      children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: line })
                    },
                    `resp-body-${idx}`
                  ));
                } catch {
                  return /* @__PURE__ */ jsx11(Box11, { marginTop: 1, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: selectedRequest.responseBody }) });
                }
              })()
            ] })
          ] }),
          focusedPane === "details" && /* @__PURE__ */ jsx11(
            Box11,
            {
              flexShrink: 0,
              height: 1,
              justifyContent: "center",
              backgroundColor: "gray",
              children: /* @__PURE__ */ jsx11(Text10, { bold: true, color: "white", children: "\u2191/\u2193 to scroll, + to zoom" })
            }
          )
        ] }) : /* @__PURE__ */ jsx11(Box11, { paddingY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: "Select a request to view details" }) })
      }
    )
  ] });
}

// src/components/ToolTestModal.tsx
import React25, { useState as useState19 } from "react";
import { Box as Box23, Text as Text20, useInput as useInput17 } from "ink";

// node_modules/ink-form/lib/Form.js
import { Box as Box22, useFocusManager, useInput as useInput16 } from "ink";
import { useEffect as useEffect19, useMemo as useMemo2, useState as useState18 } from "react";
import * as React23 from "react";

// node_modules/ink-form/lib/FormHeader.js
import * as React9 from "react";
import { Box as Box12, Text as Text11, useInput as useInput9 } from "ink";
var FormHeader = (props) => {
  const sections = props.form.sections;
  useInput9((input, key) => {
    let id = void 0;
    if (key.rightArrow) {
      id = props.currentTab + 1;
    } else if (key.leftArrow) {
      id = props.currentTab - 1;
    } else if (/\d/.test(input)) {
      id = parseInt(input) - 1;
    }
    if (id !== void 0 && id >= 0 && id < sections.length) {
      props.onChangeTab(id);
    }
  }, { isActive: !props.editingField });
  return React9.createElement(
    Box12,
    { borderStyle: "double", width: "100%", flexDirection: "column" },
    React9.createElement(
      Box12,
      { width: "100%" },
      React9.createElement(
        Box12,
        { flexGrow: 1 },
        React9.createElement(Text11, { bold: true }, props.form.title)
      ),
      React9.createElement(
        Box12,
        null,
        React9.createElement(Text11, null, !props.editingField ? "Use arrow keys to move around" : "Press ESC to cancel, or Enter to complete field")
      )
    ),
    React9.createElement(
      Box12,
      { width: "100%" },
      React9.createElement(Box12, null, !props.editingField ? sections.map((section, id) => React9.createElement(
        Box12,
        { key: section.title },
        React9.createElement(
          Text11,
          { color: "gray" },
          "[",
          id + 1,
          "] "
        ),
        React9.createElement(Text11, { color: props.currentTab === id ? "blue" : void 0, underline: props.currentTab === id }, section.title),
        React9.createElement(Text11, null, " ")
      )) : React9.createElement(
        Box12,
        null,
        React9.createElement(
          Text11,
          null,
          "Editing ",
          props.editingField
        )
      ))
    )
  );
};

// node_modules/ink-form/lib/FormFieldRenderer.js
import React21, { useState as useState17 } from "react";
import { Box as Box20, useFocus, Text as Text18, useInput as useInput14 } from "ink";

// node_modules/ink-form/lib/managers/FloatFormFieldManager.js
import React12 from "react";

// node_modules/ink-form/lib/NumberFieldRenderer.js
import React11 from "react";
import { Box as Box13, Text as Text13, useInput as useInput11 } from "ink";

// node_modules/ink-text-input/build/index.js
import React10, { useState as useState15, useEffect as useEffect17 } from "react";
import { Text as Text12, useInput as useInput10 } from "ink";

// node_modules/chalk/source/vendor/ansi-styles/index.js
var ANSI_BACKGROUND_OFFSET = 10;
var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
var styles = {
  modifier: {
    reset: [0, 0],
    // 21 isn't widely supported and 22 does the same thing
    bold: [1, 22],
    dim: [2, 22],
    italic: [3, 23],
    underline: [4, 24],
    overline: [53, 55],
    inverse: [7, 27],
    hidden: [8, 28],
    strikethrough: [9, 29]
  },
  color: {
    black: [30, 39],
    red: [31, 39],
    green: [32, 39],
    yellow: [33, 39],
    blue: [34, 39],
    magenta: [35, 39],
    cyan: [36, 39],
    white: [37, 39],
    // Bright color
    blackBright: [90, 39],
    gray: [90, 39],
    // Alias of `blackBright`
    grey: [90, 39],
    // Alias of `blackBright`
    redBright: [91, 39],
    greenBright: [92, 39],
    yellowBright: [93, 39],
    blueBright: [94, 39],
    magentaBright: [95, 39],
    cyanBright: [96, 39],
    whiteBright: [97, 39]
  },
  bgColor: {
    bgBlack: [40, 49],
    bgRed: [41, 49],
    bgGreen: [42, 49],
    bgYellow: [43, 49],
    bgBlue: [44, 49],
    bgMagenta: [45, 49],
    bgCyan: [46, 49],
    bgWhite: [47, 49],
    // Bright color
    bgBlackBright: [100, 49],
    bgGray: [100, 49],
    // Alias of `bgBlackBright`
    bgGrey: [100, 49],
    // Alias of `bgBlackBright`
    bgRedBright: [101, 49],
    bgGreenBright: [102, 49],
    bgYellowBright: [103, 49],
    bgBlueBright: [104, 49],
    bgMagentaBright: [105, 49],
    bgCyanBright: [106, 49],
    bgWhiteBright: [107, 49]
  }
};
var modifierNames = Object.keys(styles.modifier);
var foregroundColorNames = Object.keys(styles.color);
var backgroundColorNames = Object.keys(styles.bgColor);
var colorNames = [...foregroundColorNames, ...backgroundColorNames];
function assembleStyles() {
  const codes = /* @__PURE__ */ new Map();
  for (const [groupName, group] of Object.entries(styles)) {
    for (const [styleName, style] of Object.entries(group)) {
      styles[styleName] = {
        open: `\x1B[${style[0]}m`,
        close: `\x1B[${style[1]}m`
      };
      group[styleName] = styles[styleName];
      codes.set(style[0], style[1]);
    }
    Object.defineProperty(styles, groupName, {
      value: group,
      enumerable: false
    });
  }
  Object.defineProperty(styles, "codes", {
    value: codes,
    enumerable: false
  });
  styles.color.close = "\x1B[39m";
  styles.bgColor.close = "\x1B[49m";
  styles.color.ansi = wrapAnsi16();
  styles.color.ansi256 = wrapAnsi256();
  styles.color.ansi16m = wrapAnsi16m();
  styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
  styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
  styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
  Object.defineProperties(styles, {
    rgbToAnsi256: {
      value(red, green, blue) {
        if (red === green && green === blue) {
          if (red < 8) {
            return 16;
          }
          if (red > 248) {
            return 231;
          }
          return Math.round((red - 8) / 247 * 24) + 232;
        }
        return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
      },
      enumerable: false
    },
    hexToRgb: {
      value(hex) {
        const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
        if (!matches) {
          return [0, 0, 0];
        }
        let [colorString] = matches;
        if (colorString.length === 3) {
          colorString = [...colorString].map((character) => character + character).join("");
        }
        const integer = Number.parseInt(colorString, 16);
        return [
          /* eslint-disable no-bitwise */
          integer >> 16 & 255,
          integer >> 8 & 255,
          integer & 255
          /* eslint-enable no-bitwise */
        ];
      },
      enumerable: false
    },
    hexToAnsi256: {
      value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
      enumerable: false
    },
    ansi256ToAnsi: {
      value(code) {
        if (code < 8) {
          return 30 + code;
        }
        if (code < 16) {
          return 90 + (code - 8);
        }
        let red;
        let green;
        let blue;
        if (code >= 232) {
          red = ((code - 232) * 10 + 8) / 255;
          green = red;
          blue = red;
        } else {
          code -= 16;
          const remainder = code % 36;
          red = Math.floor(code / 36) / 5;
          green = Math.floor(remainder / 6) / 5;
          blue = remainder % 6 / 5;
        }
        const value = Math.max(red, green, blue) * 2;
        if (value === 0) {
          return 30;
        }
        let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
        if (value === 2) {
          result += 60;
        }
        return result;
      },
      enumerable: false
    },
    rgbToAnsi: {
      value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
      enumerable: false
    },
    hexToAnsi: {
      value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
      enumerable: false
    }
  });
  return styles;
}
var ansiStyles = assembleStyles();
var ansi_styles_default = ansiStyles;

// node_modules/chalk/source/vendor/supports-color/index.js
import process2 from "process";
import os from "os";
import tty from "tty";
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
  const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
  const position = argv.indexOf(prefix + flag);
  const terminatorPosition = argv.indexOf("--");
  return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
}
var { env } = process2;
var flagForceColor;
if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
  flagForceColor = 0;
} else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
  flagForceColor = 1;
}
function envForceColor() {
  if ("FORCE_COLOR" in env) {
    if (env.FORCE_COLOR === "true") {
      return 1;
    }
    if (env.FORCE_COLOR === "false") {
      return 0;
    }
    return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
  }
}
function translateLevel(level) {
  if (level === 0) {
    return false;
  }
  return {
    level,
    hasBasic: true,
    has256: level >= 2,
    has16m: level >= 3
  };
}
function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
  const noFlagForceColor = envForceColor();
  if (noFlagForceColor !== void 0) {
    flagForceColor = noFlagForceColor;
  }
  const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
  if (forceColor === 0) {
    return 0;
  }
  if (sniffFlags) {
    if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
      return 3;
    }
    if (hasFlag("color=256")) {
      return 2;
    }
  }
  if ("TF_BUILD" in env && "AGENT_NAME" in env) {
    return 1;
  }
  if (haveStream && !streamIsTTY && forceColor === void 0) {
    return 0;
  }
  const min = forceColor || 0;
  if (env.TERM === "dumb") {
    return min;
  }
  if (process2.platform === "win32") {
    const osRelease = os.release().split(".");
    if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
      return Number(osRelease[2]) >= 14931 ? 3 : 2;
    }
    return 1;
  }
  if ("CI" in env) {
    if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => key in env)) {
      return 3;
    }
    if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") {
      return 1;
    }
    return min;
  }
  if ("TEAMCITY_VERSION" in env) {
    return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
  }
  if (env.COLORTERM === "truecolor") {
    return 3;
  }
  if (env.TERM === "xterm-kitty") {
    return 3;
  }
  if (env.TERM === "xterm-ghostty") {
    return 3;
  }
  if (env.TERM === "wezterm") {
    return 3;
  }
  if ("TERM_PROGRAM" in env) {
    const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
    switch (env.TERM_PROGRAM) {
      case "iTerm.app": {
        return version >= 3 ? 3 : 2;
      }
      case "Apple_Terminal": {
        return 2;
      }
    }
  }
  if (/-256(color)?$/i.test(env.TERM)) {
    return 2;
  }
  if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
    return 1;
  }
  if ("COLORTERM" in env) {
    return 1;
  }
  return min;
}
function createSupportsColor(stream, options = {}) {
  const level = _supportsColor(stream, {
    streamIsTTY: stream && stream.isTTY,
    ...options
  });
  return translateLevel(level);
}
var supportsColor = {
  stdout: createSupportsColor({ isTTY: tty.isatty(1) }),
  stderr: createSupportsColor({ isTTY: tty.isatty(2) })
};
var supports_color_default = supportsColor;

// node_modules/chalk/source/utilities.js
function stringReplaceAll(string, substring, replacer) {
  let index = string.indexOf(substring);
  if (index === -1) {
    return string;
  }
  const substringLength = substring.length;
  let endIndex = 0;
  let returnValue = "";
  do {
    returnValue += string.slice(endIndex, index) + substring + replacer;
    endIndex = index + substringLength;
    index = string.indexOf(substring, endIndex);
  } while (index !== -1);
  returnValue += string.slice(endIndex);
  return returnValue;
}
function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
  let endIndex = 0;
  let returnValue = "";
  do {
    const gotCR = string[index - 1] === "\r";
    returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
    endIndex = index + 1;
    index = string.indexOf("\n", endIndex);
  } while (index !== -1);
  returnValue += string.slice(endIndex);
  return returnValue;
}

// node_modules/chalk/source/index.js
var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
var GENERATOR = /* @__PURE__ */ Symbol("GENERATOR");
var STYLER = /* @__PURE__ */ Symbol("STYLER");
var IS_EMPTY = /* @__PURE__ */ Symbol("IS_EMPTY");
var levelMapping = [
  "ansi",
  "ansi",
  "ansi256",
  "ansi16m"
];
var styles2 = /* @__PURE__ */ Object.create(null);
var applyOptions = (object, options = {}) => {
  if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
    throw new Error("The `level` option should be an integer from 0 to 3");
  }
  const colorLevel = stdoutColor ? stdoutColor.level : 0;
  object.level = options.level === void 0 ? colorLevel : options.level;
};
var chalkFactory = (options) => {
  const chalk2 = (...strings) => strings.join(" ");
  applyOptions(chalk2, options);
  Object.setPrototypeOf(chalk2, createChalk.prototype);
  return chalk2;
};
function createChalk(options) {
  return chalkFactory(options);
}
Object.setPrototypeOf(createChalk.prototype, Function.prototype);
for (const [styleName, style] of Object.entries(ansi_styles_default)) {
  styles2[styleName] = {
    get() {
      const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
      Object.defineProperty(this, styleName, { value: builder });
      return builder;
    }
  };
}
styles2.visible = {
  get() {
    const builder = createBuilder(this, this[STYLER], true);
    Object.defineProperty(this, "visible", { value: builder });
    return builder;
  }
};
var getModelAnsi = (model, level, type, ...arguments_) => {
  if (model === "rgb") {
    if (level === "ansi16m") {
      return ansi_styles_default[type].ansi16m(...arguments_);
    }
    if (level === "ansi256") {
      return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
    }
    return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
  }
  if (model === "hex") {
    return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
  }
  return ansi_styles_default[type][model](...arguments_);
};
var usedModels = ["rgb", "hex", "ansi256"];
for (const model of usedModels) {
  styles2[model] = {
    get() {
      const { level } = this;
      return function(...arguments_) {
        const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
        return createBuilder(this, styler, this[IS_EMPTY]);
      };
    }
  };
  const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
  styles2[bgModel] = {
    get() {
      const { level } = this;
      return function(...arguments_) {
        const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
        return createBuilder(this, styler, this[IS_EMPTY]);
      };
    }
  };
}
var proto = Object.defineProperties(() => {
}, {
  ...styles2,
  level: {
    enumerable: true,
    get() {
      return this[GENERATOR].level;
    },
    set(level) {
      this[GENERATOR].level = level;
    }
  }
});
var createStyler = (open2, close, parent) => {
  let openAll;
  let closeAll;
  if (parent === void 0) {
    openAll = open2;
    closeAll = close;
  } else {
    openAll = parent.openAll + open2;
    closeAll = close + parent.closeAll;
  }
  return {
    open: open2,
    close,
    openAll,
    closeAll,
    parent
  };
};
var createBuilder = (self, _styler, _isEmpty) => {
  const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
  Object.setPrototypeOf(builder, proto);
  builder[GENERATOR] = self;
  builder[STYLER] = _styler;
  builder[IS_EMPTY] = _isEmpty;
  return builder;
};
var applyStyle = (self, string) => {
  if (self.level <= 0 || !string) {
    return self[IS_EMPTY] ? "" : string;
  }
  let styler = self[STYLER];
  if (styler === void 0) {
    return string;
  }
  const { openAll, closeAll } = styler;
  if (string.includes("\x1B")) {
    while (styler !== void 0) {
      string = stringReplaceAll(string, styler.close, styler.open);
      styler = styler.parent;
    }
  }
  const lfIndex = string.indexOf("\n");
  if (lfIndex !== -1) {
    string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
  }
  return openAll + string + closeAll;
};
Object.defineProperties(createChalk.prototype, styles2);
var chalk = createChalk();
var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
var source_default = chalk;

// node_modules/ink-text-input/build/index.js
function TextInput({ value: originalValue, placeholder = "", focus = true, mask, highlightPastedText = false, showCursor = true, onChange, onSubmit }) {
  const [state, setState] = useState15({
    cursorOffset: (originalValue || "").length,
    cursorWidth: 0
  });
  const { cursorOffset, cursorWidth } = state;
  useEffect17(() => {
    setState((previousState) => {
      if (!focus || !showCursor) {
        return previousState;
      }
      const newValue = originalValue || "";
      if (previousState.cursorOffset > newValue.length - 1) {
        return {
          cursorOffset: newValue.length,
          cursorWidth: 0
        };
      }
      return previousState;
    });
  }, [originalValue, focus, showCursor]);
  const cursorActualWidth = highlightPastedText ? cursorWidth : 0;
  const value = mask ? mask.repeat(originalValue.length) : originalValue;
  let renderedValue = value;
  let renderedPlaceholder = placeholder ? source_default.grey(placeholder) : void 0;
  if (showCursor && focus) {
    renderedPlaceholder = placeholder.length > 0 ? source_default.inverse(placeholder[0]) + source_default.grey(placeholder.slice(1)) : source_default.inverse(" ");
    renderedValue = value.length > 0 ? "" : source_default.inverse(" ");
    let i = 0;
    for (const char of value) {
      renderedValue += i >= cursorOffset - cursorActualWidth && i <= cursorOffset ? source_default.inverse(char) : char;
      i++;
    }
    if (value.length > 0 && cursorOffset === value.length) {
      renderedValue += source_default.inverse(" ");
    }
  }
  useInput10((input, key) => {
    if (key.upArrow || key.downArrow || key.ctrl && input === "c" || key.tab || key.shift && key.tab) {
      return;
    }
    if (key.return) {
      if (onSubmit) {
        onSubmit(originalValue);
      }
      return;
    }
    let nextCursorOffset = cursorOffset;
    let nextValue = originalValue;
    let nextCursorWidth = 0;
    if (key.leftArrow) {
      if (showCursor) {
        nextCursorOffset--;
      }
    } else if (key.rightArrow) {
      if (showCursor) {
        nextCursorOffset++;
      }
    } else if (key.backspace || key.delete) {
      if (cursorOffset > 0) {
        nextValue = originalValue.slice(0, cursorOffset - 1) + originalValue.slice(cursorOffset, originalValue.length);
        nextCursorOffset--;
      }
    } else {
      nextValue = originalValue.slice(0, cursorOffset) + input + originalValue.slice(cursorOffset, originalValue.length);
      nextCursorOffset += input.length;
      if (input.length > 1) {
        nextCursorWidth = input.length;
      }
    }
    if (cursorOffset < 0) {
      nextCursorOffset = 0;
    }
    if (cursorOffset > originalValue.length) {
      nextCursorOffset = originalValue.length;
    }
    setState({
      cursorOffset: nextCursorOffset,
      cursorWidth: nextCursorWidth
    });
    if (nextValue !== originalValue) {
      onChange(nextValue);
    }
  }, { isActive: focus });
  return React10.createElement(Text12, null, placeholder ? value.length > 0 ? renderedValue : renderedPlaceholder : renderedValue);
}
var build_default = TextInput;

// node_modules/ink-form/lib/NumberFieldRenderer.js
var FLOAT_REGEX = /^-?((\d+)|(\d*\.\d+))$/;
var INTEGER_REGEX = /^-?\d+$/;
var NumberFieldRenderer = (props) => {
  const regex = props.isFloat ? FLOAT_REGEX : INTEGER_REGEX;
  const parse2 = props.isFloat ? parseFloat : parseInt;
  const change = (value) => {
    if (regex.test(value)) {
      props.onClearError();
      const asNumber = Math.round(parse2(value) * 1e5) / 1e5;
      if (props.field.min !== void 0 && props.field.min > asNumber) {
        props.onError(`"${value}" too small, must be above or equal to ${props.field.min}.`);
        props.onChange(value);
        return;
      } else if (props.field.max !== void 0 && props.field.max < asNumber) {
        props.onError(`"${value}" too big, must be below or equal to ${props.field.max}.`);
        props.onChange(value);
        return;
      } else {
        props.onChange(asNumber);
      }
    } else {
      props.onError(`"${value}" is not an ${props.isFloat ? "float" : "integer"}.`);
      props.onChange(value);
    }
  };
  useInput11((input, key) => {
    if (typeof props.value === "number") {
      if (key.upArrow) {
        change("" + ((props.value ?? 0) + (props.field.step ?? 1)));
      } else if (key.downArrow) {
        change("" + ((props.value ?? 0) - (props.field.step ?? 1)));
      }
    } else {
      if (key.upArrow || key.downArrow) {
        change("" + ((props.field.min ?? 0) <= 0 && (props.field.max ?? 0) >= 0 ? 0 : props.field.min ?? props.field.max));
      }
    }
  });
  return React11.createElement(
    Box13,
    { borderStyle: "round", width: "100%", flexDirection: "column" },
    React11.createElement(
      Box13,
      null,
      React11.createElement(build_default, { value: "" + (props.value ?? ""), onChange: (value) => {
        if (regex.test(value)) {
          props.onClearError();
          change(value);
        } else {
          props.onError(`"${value}" is not an ${props.isFloat ? "float" : "integer"}.`);
          props.onChange(value);
        }
      }, placeholder: props.field.placeholder, onSubmit: () => props.onSetEditingField(void 0) })
    ),
    React11.createElement(
      Box13,
      null,
      React11.createElement(Text13, { dimColor: true }, "Press UP/DOWN to increase or decrease the value.")
    )
  );
};

// node_modules/ink-form/lib/managers/FloatFormFieldManager.js
var FloatFormFieldManager = class {
  type = "float";
  renderField = (props) => React12.createElement(NumberFieldRenderer, { ...props, isFloat: true });
  renderValue = (props) => React12.createElement(React12.Fragment, null, props.value);
};

// node_modules/ink-form/lib/managers/IntegerFormFieldManager.js
import React13 from "react";
var IntegerFormFieldManager = class {
  type = "integer";
  renderField = (props) => React13.createElement(NumberFieldRenderer, { ...props, isFloat: false });
  renderValue = (props) => React13.createElement(React13.Fragment, null, props.value);
};

// node_modules/ink-form/lib/managers/SelectFormFieldManager.js
import React17 from "react";
import { Box as Box16 } from "ink";

// node_modules/ink-select-input/build/Indicator.js
import React14 from "react";
import { Box as Box14, Text as Text14 } from "ink";

// node_modules/is-unicode-supported/index.js
import process3 from "process";
function isUnicodeSupported() {
  const { env: env2 } = process3;
  const { TERM, TERM_PROGRAM } = env2;
  if (process3.platform !== "win32") {
    return TERM !== "linux";
  }
  return Boolean(env2.WT_SESSION) || Boolean(env2.TERMINUS_SUBLIME) || env2.ConEmuTask === "{cmd::Cmder}" || TERM_PROGRAM === "Terminus-Sublime" || TERM_PROGRAM === "vscode" || TERM === "xterm-256color" || TERM === "alacritty" || TERM === "rxvt-unicode" || TERM === "rxvt-unicode-256color" || env2.TERMINAL_EMULATOR === "JetBrains-JediTerm";
}

// node_modules/figures/index.js
var common = {
  circleQuestionMark: "(?)",
  questionMarkPrefix: "(?)",
  square: "\u2588",
  squareDarkShade: "\u2593",
  squareMediumShade: "\u2592",
  squareLightShade: "\u2591",
  squareTop: "\u2580",
  squareBottom: "\u2584",
  squareLeft: "\u258C",
  squareRight: "\u2590",
  squareCenter: "\u25A0",
  bullet: "\u25CF",
  dot: "\u2024",
  ellipsis: "\u2026",
  pointerSmall: "\u203A",
  triangleUp: "\u25B2",
  triangleUpSmall: "\u25B4",
  triangleDown: "\u25BC",
  triangleDownSmall: "\u25BE",
  triangleLeftSmall: "\u25C2",
  triangleRightSmall: "\u25B8",
  home: "\u2302",
  heart: "\u2665",
  musicNote: "\u266A",
  musicNoteBeamed: "\u266B",
  arrowUp: "\u2191",
  arrowDown: "\u2193",
  arrowLeft: "\u2190",
  arrowRight: "\u2192",
  arrowLeftRight: "\u2194",
  arrowUpDown: "\u2195",
  almostEqual: "\u2248",
  notEqual: "\u2260",
  lessOrEqual: "\u2264",
  greaterOrEqual: "\u2265",
  identical: "\u2261",
  infinity: "\u221E",
  subscriptZero: "\u2080",
  subscriptOne: "\u2081",
  subscriptTwo: "\u2082",
  subscriptThree: "\u2083",
  subscriptFour: "\u2084",
  subscriptFive: "\u2085",
  subscriptSix: "\u2086",
  subscriptSeven: "\u2087",
  subscriptEight: "\u2088",
  subscriptNine: "\u2089",
  oneHalf: "\xBD",
  oneThird: "\u2153",
  oneQuarter: "\xBC",
  oneFifth: "\u2155",
  oneSixth: "\u2159",
  oneEighth: "\u215B",
  twoThirds: "\u2154",
  twoFifths: "\u2156",
  threeQuarters: "\xBE",
  threeFifths: "\u2157",
  threeEighths: "\u215C",
  fourFifths: "\u2158",
  fiveSixths: "\u215A",
  fiveEighths: "\u215D",
  sevenEighths: "\u215E",
  line: "\u2500",
  lineBold: "\u2501",
  lineDouble: "\u2550",
  lineDashed0: "\u2504",
  lineDashed1: "\u2505",
  lineDashed2: "\u2508",
  lineDashed3: "\u2509",
  lineDashed4: "\u254C",
  lineDashed5: "\u254D",
  lineDashed6: "\u2574",
  lineDashed7: "\u2576",
  lineDashed8: "\u2578",
  lineDashed9: "\u257A",
  lineDashed10: "\u257C",
  lineDashed11: "\u257E",
  lineDashed12: "\u2212",
  lineDashed13: "\u2013",
  lineDashed14: "\u2010",
  lineDashed15: "\u2043",
  lineVertical: "\u2502",
  lineVerticalBold: "\u2503",
  lineVerticalDouble: "\u2551",
  lineVerticalDashed0: "\u2506",
  lineVerticalDashed1: "\u2507",
  lineVerticalDashed2: "\u250A",
  lineVerticalDashed3: "\u250B",
  lineVerticalDashed4: "\u254E",
  lineVerticalDashed5: "\u254F",
  lineVerticalDashed6: "\u2575",
  lineVerticalDashed7: "\u2577",
  lineVerticalDashed8: "\u2579",
  lineVerticalDashed9: "\u257B",
  lineVerticalDashed10: "\u257D",
  lineVerticalDashed11: "\u257F",
  lineDownLeft: "\u2510",
  lineDownLeftArc: "\u256E",
  lineDownBoldLeftBold: "\u2513",
  lineDownBoldLeft: "\u2512",
  lineDownLeftBold: "\u2511",
  lineDownDoubleLeftDouble: "\u2557",
  lineDownDoubleLeft: "\u2556",
  lineDownLeftDouble: "\u2555",
  lineDownRight: "\u250C",
  lineDownRightArc: "\u256D",
  lineDownBoldRightBold: "\u250F",
  lineDownBoldRight: "\u250E",
  lineDownRightBold: "\u250D",
  lineDownDoubleRightDouble: "\u2554",
  lineDownDoubleRight: "\u2553",
  lineDownRightDouble: "\u2552",
  lineUpLeft: "\u2518",
  lineUpLeftArc: "\u256F",
  lineUpBoldLeftBold: "\u251B",
  lineUpBoldLeft: "\u251A",
  lineUpLeftBold: "\u2519",
  lineUpDoubleLeftDouble: "\u255D",
  lineUpDoubleLeft: "\u255C",
  lineUpLeftDouble: "\u255B",
  lineUpRight: "\u2514",
  lineUpRightArc: "\u2570",
  lineUpBoldRightBold: "\u2517",
  lineUpBoldRight: "\u2516",
  lineUpRightBold: "\u2515",
  lineUpDoubleRightDouble: "\u255A",
  lineUpDoubleRight: "\u2559",
  lineUpRightDouble: "\u2558",
  lineUpDownLeft: "\u2524",
  lineUpBoldDownBoldLeftBold: "\u252B",
  lineUpBoldDownBoldLeft: "\u2528",
  lineUpDownLeftBold: "\u2525",
  lineUpBoldDownLeftBold: "\u2529",
  lineUpDownBoldLeftBold: "\u252A",
  lineUpDownBoldLeft: "\u2527",
  lineUpBoldDownLeft: "\u2526",
  lineUpDoubleDownDoubleLeftDouble: "\u2563",
  lineUpDoubleDownDoubleLeft: "\u2562",
  lineUpDownLeftDouble: "\u2561",
  lineUpDownRight: "\u251C",
  lineUpBoldDownBoldRightBold: "\u2523",
  lineUpBoldDownBoldRight: "\u2520",
  lineUpDownRightBold: "\u251D",
  lineUpBoldDownRightBold: "\u2521",
  lineUpDownBoldRightBold: "\u2522",
  lineUpDownBoldRight: "\u251F",
  lineUpBoldDownRight: "\u251E",
  lineUpDoubleDownDoubleRightDouble: "\u2560",
  lineUpDoubleDownDoubleRight: "\u255F",
  lineUpDownRightDouble: "\u255E",
  lineDownLeftRight: "\u252C",
  lineDownBoldLeftBoldRightBold: "\u2533",
  lineDownLeftBoldRightBold: "\u252F",
  lineDownBoldLeftRight: "\u2530",
  lineDownBoldLeftBoldRight: "\u2531",
  lineDownBoldLeftRightBold: "\u2532",
  lineDownLeftRightBold: "\u252E",
  lineDownLeftBoldRight: "\u252D",
  lineDownDoubleLeftDoubleRightDouble: "\u2566",
  lineDownDoubleLeftRight: "\u2565",
  lineDownLeftDoubleRightDouble: "\u2564",
  lineUpLeftRight: "\u2534",
  lineUpBoldLeftBoldRightBold: "\u253B",
  lineUpLeftBoldRightBold: "\u2537",
  lineUpBoldLeftRight: "\u2538",
  lineUpBoldLeftBoldRight: "\u2539",
  lineUpBoldLeftRightBold: "\u253A",
  lineUpLeftRightBold: "\u2536",
  lineUpLeftBoldRight: "\u2535",
  lineUpDoubleLeftDoubleRightDouble: "\u2569",
  lineUpDoubleLeftRight: "\u2568",
  lineUpLeftDoubleRightDouble: "\u2567",
  lineUpDownLeftRight: "\u253C",
  lineUpBoldDownBoldLeftBoldRightBold: "\u254B",
  lineUpDownBoldLeftBoldRightBold: "\u2548",
  lineUpBoldDownLeftBoldRightBold: "\u2547",
  lineUpBoldDownBoldLeftRightBold: "\u254A",
  lineUpBoldDownBoldLeftBoldRight: "\u2549",
  lineUpBoldDownLeftRight: "\u2540",
  lineUpDownBoldLeftRight: "\u2541",
  lineUpDownLeftBoldRight: "\u253D",
  lineUpDownLeftRightBold: "\u253E",
  lineUpBoldDownBoldLeftRight: "\u2542",
  lineUpDownLeftBoldRightBold: "\u253F",
  lineUpBoldDownLeftBoldRight: "\u2543",
  lineUpBoldDownLeftRightBold: "\u2544",
  lineUpDownBoldLeftBoldRight: "\u2545",
  lineUpDownBoldLeftRightBold: "\u2546",
  lineUpDoubleDownDoubleLeftDoubleRightDouble: "\u256C",
  lineUpDoubleDownDoubleLeftRight: "\u256B",
  lineUpDownLeftDoubleRightDouble: "\u256A",
  lineCross: "\u2573",
  lineBackslash: "\u2572",
  lineSlash: "\u2571"
};
var specialMainSymbols = {
  tick: "\u2714",
  info: "\u2139",
  warning: "\u26A0",
  cross: "\u2718",
  squareSmall: "\u25FB",
  squareSmallFilled: "\u25FC",
  circle: "\u25EF",
  circleFilled: "\u25C9",
  circleDotted: "\u25CC",
  circleDouble: "\u25CE",
  circleCircle: "\u24DE",
  circleCross: "\u24E7",
  circlePipe: "\u24BE",
  radioOn: "\u25C9",
  radioOff: "\u25EF",
  checkboxOn: "\u2612",
  checkboxOff: "\u2610",
  checkboxCircleOn: "\u24E7",
  checkboxCircleOff: "\u24BE",
  pointer: "\u276F",
  triangleUpOutline: "\u25B3",
  triangleLeft: "\u25C0",
  triangleRight: "\u25B6",
  lozenge: "\u25C6",
  lozengeOutline: "\u25C7",
  hamburger: "\u2630",
  smiley: "\u32E1",
  mustache: "\u0DF4",
  star: "\u2605",
  play: "\u25B6",
  nodejs: "\u2B22",
  oneSeventh: "\u2150",
  oneNinth: "\u2151",
  oneTenth: "\u2152"
};
var specialFallbackSymbols = {
  tick: "\u221A",
  info: "i",
  warning: "\u203C",
  cross: "\xD7",
  squareSmall: "\u25A1",
  squareSmallFilled: "\u25A0",
  circle: "( )",
  circleFilled: "(*)",
  circleDotted: "( )",
  circleDouble: "( )",
  circleCircle: "(\u25CB)",
  circleCross: "(\xD7)",
  circlePipe: "(\u2502)",
  radioOn: "(*)",
  radioOff: "( )",
  checkboxOn: "[\xD7]",
  checkboxOff: "[ ]",
  checkboxCircleOn: "(\xD7)",
  checkboxCircleOff: "( )",
  pointer: ">",
  triangleUpOutline: "\u2206",
  triangleLeft: "\u25C4",
  triangleRight: "\u25BA",
  lozenge: "\u2666",
  lozengeOutline: "\u25CA",
  hamburger: "\u2261",
  smiley: "\u263A",
  mustache: "\u250C\u2500\u2510",
  star: "\u2736",
  play: "\u25BA",
  nodejs: "\u2666",
  oneSeventh: "1/7",
  oneNinth: "1/9",
  oneTenth: "1/10"
};
var mainSymbols = { ...common, ...specialMainSymbols };
var fallbackSymbols = { ...common, ...specialFallbackSymbols };
var shouldUseMain = isUnicodeSupported();
var figures = shouldUseMain ? mainSymbols : fallbackSymbols;
var figures_default = figures;
var replacements = Object.entries(specialMainSymbols);

// node_modules/ink-select-input/build/Indicator.js
function Indicator({ isSelected = false }) {
  return React14.createElement(Box14, { marginRight: 1 }, isSelected ? React14.createElement(Text14, { color: "blue" }, figures_default.pointer) : React14.createElement(Text14, null, " "));
}
var Indicator_default = Indicator;

// node_modules/ink-select-input/build/Item.js
import * as React15 from "react";
import { Text as Text15 } from "ink";
function Item({ isSelected = false, label }) {
  return React15.createElement(Text15, { color: isSelected ? "blue" : void 0 }, label);
}
var Item_default = Item;

// node_modules/ink-select-input/build/SelectInput.js
import { isDeepStrictEqual } from "util";
import React16, { useState as useState16, useEffect as useEffect18, useRef as useRef11, useCallback as useCallback10 } from "react";

// node_modules/to-rotated/index.js
function toRotated(array, steps) {
  if (!Array.isArray(array)) {
    throw new TypeError(`Expected an array, got \`${typeof array}\`.`);
  }
  if (!Number.isSafeInteger(steps)) {
    throw new TypeError(`The \`steps\` parameter must be an integer, got ${steps}.`);
  }
  const { length } = array;
  if (length === 0) {
    return [...array];
  }
  const normalizedSteps = (steps % length + length) % length;
  if (normalizedSteps === 0) {
    return [...array];
  }
  return [
    ...array.slice(-normalizedSteps),
    ...array.slice(0, -normalizedSteps)
  ];
}

// node_modules/ink-select-input/build/SelectInput.js
import { Box as Box15, useInput as useInput12 } from "ink";
function SelectInput({ items = [], isFocused = true, initialIndex = 0, indicatorComponent = Indicator_default, itemComponent = Item_default, limit: customLimit, onSelect, onHighlight }) {
  const hasLimit = typeof customLimit === "number" && items.length > customLimit;
  const limit = hasLimit ? Math.min(customLimit, items.length) : items.length;
  const lastIndex = limit - 1;
  const [rotateIndex, setRotateIndex] = useState16(initialIndex > lastIndex ? lastIndex - initialIndex : 0);
  const [selectedIndex, setSelectedIndex] = useState16(initialIndex ? initialIndex > lastIndex ? lastIndex : initialIndex : 0);
  const previousItems = useRef11(items);
  useEffect18(() => {
    if (!isDeepStrictEqual(previousItems.current.map((item) => item.value), items.map((item) => item.value))) {
      setRotateIndex(0);
      setSelectedIndex(0);
    }
    previousItems.current = items;
  }, [items]);
  useInput12(useCallback10((input, key) => {
    if (input === "k" || key.upArrow) {
      const lastIndex2 = (hasLimit ? limit : items.length) - 1;
      const atFirstIndex = selectedIndex === 0;
      const nextIndex = hasLimit ? selectedIndex : lastIndex2;
      const nextRotateIndex = atFirstIndex ? rotateIndex + 1 : rotateIndex;
      const nextSelectedIndex = atFirstIndex ? nextIndex : selectedIndex - 1;
      setRotateIndex(nextRotateIndex);
      setSelectedIndex(nextSelectedIndex);
      const slicedItems2 = hasLimit ? toRotated(items, nextRotateIndex).slice(0, limit) : items;
      if (typeof onHighlight === "function") {
        onHighlight(slicedItems2[nextSelectedIndex]);
      }
    }
    if (input === "j" || key.downArrow) {
      const atLastIndex = selectedIndex === (hasLimit ? limit : items.length) - 1;
      const nextIndex = hasLimit ? selectedIndex : 0;
      const nextRotateIndex = atLastIndex ? rotateIndex - 1 : rotateIndex;
      const nextSelectedIndex = atLastIndex ? nextIndex : selectedIndex + 1;
      setRotateIndex(nextRotateIndex);
      setSelectedIndex(nextSelectedIndex);
      const slicedItems2 = hasLimit ? toRotated(items, nextRotateIndex).slice(0, limit) : items;
      if (typeof onHighlight === "function") {
        onHighlight(slicedItems2[nextSelectedIndex]);
      }
    }
    if (/^[1-9]$/.test(input)) {
      const targetIndex = Number.parseInt(input, 10) - 1;
      const visibleItems = hasLimit ? toRotated(items, rotateIndex).slice(0, limit) : items;
      if (targetIndex >= 0 && targetIndex < visibleItems.length) {
        const selectedItem = visibleItems[targetIndex];
        if (selectedItem) {
          onSelect?.(selectedItem);
        }
      }
    }
    if (key.return) {
      const slicedItems2 = hasLimit ? toRotated(items, rotateIndex).slice(0, limit) : items;
      if (typeof onSelect === "function") {
        onSelect(slicedItems2[selectedIndex]);
      }
    }
  }, [
    hasLimit,
    limit,
    rotateIndex,
    selectedIndex,
    items,
    onSelect,
    onHighlight
  ]), { isActive: isFocused });
  const slicedItems = hasLimit ? toRotated(items, rotateIndex).slice(0, limit) : items;
  return React16.createElement(Box15, { flexDirection: "column" }, slicedItems.map((item, index) => {
    const isSelected = index === selectedIndex;
    return (
      // @ts-expect-error - `key` can't be optional but `item.value` is generic T
      React16.createElement(
        Box15,
        { key: item.key ?? item.value },
        React16.createElement(indicatorComponent, { isSelected }),
        React16.createElement(itemComponent, { ...item, isSelected })
      )
    );
  }));
}
var SelectInput_default = SelectInput;

// node_modules/ink-form/lib/managers/SelectFormFieldManager.js
var SelectFormFieldManager = class {
  type = "select";
  renderField = (props) => React17.createElement(
    Box16,
    { borderStyle: "round", width: "100%" },
    React17.createElement(SelectInput_default, { items: props.field.options.map((option) => ({ value: option.value, label: option.label ?? option.value })), onHighlight: (option) => props.onChange(option.value), initialIndex: props.field.options.findIndex((option) => option.value === props.value) ?? 0 })
  );
  renderValue = (props) => React17.createElement(React17.Fragment, null, props.field.options.find((option) => option.value === props.value)?.label ?? props.value ?? "No value");
};

// node_modules/ink-form/lib/managers/StringFormFieldManager.js
import React18 from "react";
import { Box as Box17, Transform } from "ink";
var StringFormFieldManager = class {
  type = "string";
  renderField = (props) => React18.createElement(
    Box17,
    { borderStyle: "round", width: "100%" },
    React18.createElement(build_default, { value: props.value ?? "", onChange: (value) => {
      props.onChange(value);
      if (props.field.regex && !props.field.regex.test(value)) {
        props.onError(`Value does not conform to regular expression: ${props.field.regex}`);
      } else {
        props.onClearError();
      }
    }, placeholder: props.field.placeholder, onSubmit: () => props.onSetEditingField(void 0), mask: props.field.mask })
  );
  renderValue = (props) => React18.createElement(Transform, { transform: props.value !== void 0 && props.field.mask ? (text) => text.split("").map((char) => "*").join("") : (text) => text }, props.value);
};

// node_modules/ink-form/lib/managers/BooleanFormFieldManager.js
import React19 from "react";
import { Box as Box18, Text as Text16, useInput as useInput13 } from "ink";
var BooleanFormFieldManager = class {
  type = "boolean";
  renderField = (props) => {
    useInput13((input, key) => {
      if (input === " ") {
        props.onChange(!props.value);
      }
    });
    return React19.createElement(
      Box18,
      { width: "100%", borderStyle: "round", borderColor: props.value ? "green" : "gray", marginBottom: 2 },
      React19.createElement(
        Box18,
        { marginRight: 2 },
        React19.createElement(Text16, { color: props.value ? "green" : "gray" }, props.value === void 0 ? "[Not set]" : props.value ? "[True]" : "[False]")
      ),
      React19.createElement(
        Box18,
        { flexGrow: 1 },
        React19.createElement(Text16, null, "Press Space to toggle value")
      )
    );
  };
  renderValue = (props) => React19.createElement(Text16, null, props.value === void 0 ? "[Not set]" : props.value ? "[True]" : "[False]");
};

// node_modules/ink-form/lib/managers/managers.js
var managers = [
  new FloatFormFieldManager(),
  new IntegerFormFieldManager(),
  new SelectFormFieldManager(),
  new StringFormFieldManager(),
  new BooleanFormFieldManager()
];
var getManager = (formFieldType, customManagers = []) => {
  return [...managers, ...customManagers].find((manager) => manager.type === formFieldType);
};

// node_modules/ink-form/lib/DescriptionRenderer.js
import React20 from "react";
import { Box as Box19, Text as Text17 } from "ink";
var DescriptionRenderer = (props) => {
  if (!props.description) {
    return null;
  } else if (props.description.type) {
    return props.description;
  } else if (Array.isArray(props.description)) {
    return React20.createElement(Box19, { flexDirection: "column" }, props.description.map((line) => React20.createElement(
      Box19,
      { key: line, width: "100%", marginBottom: 1 },
      React20.createElement(Text17, null, line)
    )));
  } else {
    return React20.createElement(Text17, null, props.description);
  }
};

// node_modules/ink-form/lib/FormFieldRenderer.js
var FormFieldRenderer = (props) => {
  const manager = getManager(props.field.type, props.customManagers);
  const [error, setError] = useState17();
  const [currentValue, setCurrentValue] = useState17(props.value ?? props.field.initialValue);
  const isEditing = !!props.editingField && props.editingField === (props.field.label ?? props.field.name);
  const hide = !isEditing && !!props.editingField;
  const save = (newValue) => {
    if (!error) {
      if (newValue) {
        props.onChange(newValue);
        setCurrentValue(newValue);
      } else {
        props.onChange(currentValue);
      }
      props.onSetEditingField(void 0);
    }
  };
  const cancel = () => {
    setCurrentValue(props.value);
    props.onSetEditingField(void 0);
    setError(void 0);
  };
  const { isFocused } = useFocus({});
  useInput14((input, key) => {
    if (!isEditing && key.return && !key.ctrl && !key.meta) {
      props.onSetEditingField(props.field.label ?? props.field.name);
    } else if (isEditing && key.escape) {
      cancel();
    } else if (isEditing && key.return && (!manager?.needCtrlToReturnSave || key.ctrl)) {
      save();
    }
  }, { isActive: isFocused });
  if (hide) {
    return null;
  }
  if (!isEditing) {
    const RenderValue = manager?.renderValue ?? (() => React21.createElement(React21.Fragment, null, props.value));
    return React21.createElement(
      Box20,
      { marginX: 2, paddingX: 1, borderStyle: "round", borderColor: isFocused ? "blue" : void 0 },
      React21.createElement(
        Box20,
        { flexGrow: 1 },
        React21.createElement(Text18, { underline: isFocused, color: isFocused ? "blue" : void 0 }, props.field.label ?? props.field.name),
        props.field.required && React21.createElement(Text18, { color: "red" }, "*"),
        React21.createElement(Text18, null, ": "),
        React21.createElement(
          Text18,
          { dimColor: true },
          React21.createElement(RenderValue, { value: props.value, field: props.field })
        )
      ),
      isFocused && React21.createElement(
        Box20,
        null,
        React21.createElement(Text18, null, "Press enter to edit")
      )
    );
  } else {
    let component;
    const rendererProps = {
      ...props,
      onError: setError,
      onClearError: () => setError(void 0),
      onChange: setCurrentValue,
      value: currentValue,
      onSave: save,
      onCancel: cancel,
      error
    };
    if (!manager) {
      component = React21.createElement(
        Text18,
        { color: "red" },
        "No formfield manager for form field of type ",
        props.field.type,
        " available."
      );
    } else {
      const Field = manager.renderField;
      component = React21.createElement(Field, { ...rendererProps });
    }
    return React21.createElement(
      Box20,
      { paddingX: 3, paddingY: 1, flexDirection: "column" },
      React21.createElement(
        Box20,
        null,
        React21.createElement(Text18, null, props.field.label ?? props.field.name),
        props.field.required && React21.createElement(Text18, { color: "red" }, "*"),
        React21.createElement(Text18, null, ": ")
      ),
      React21.createElement(Box20, null, component),
      props.field.description && React21.createElement(
        Box20,
        null,
        React21.createElement(
          Text18,
          { dimColor: true },
          React21.createElement(DescriptionRenderer, { description: props.field.description })
        )
      ),
      error && React21.createElement(
        Box20,
        null,
        React21.createElement(
          Text18,
          { color: "red" },
          "Error: ",
          error
        )
      ),
      React21.createElement(
        Box20,
        { marginTop: 2 },
        React21.createElement(Text18, { dimColor: true }, error ? React21.createElement(React21.Fragment, null, "Press ESC to cancel.") : React21.createElement(
          React21.Fragment,
          null,
          "Press ",
          manager?.needCtrlToReturnSave ? "CTRL+Enter" : "Enter",
          " to complete field, or ESC to cancel."
        ))
      )
    );
  }
};

// node_modules/ink-form/lib/canSubmit.js
var canSubmit = (form, value) => {
  return form.sections.map((section) => section.fields).reduce((fields1, fields2) => [...fields1, ...fields2], []).map((field) => !field.required || value[field.name] !== void 0 && value[field.name] !== "").reduce((field1, field2) => field1 && field2, true);
};

// node_modules/ink-form/lib/SubmitButton.js
import React22 from "react";
import { Text as Text19, Box as Box21, useFocus as useFocus2, useInput as useInput15 } from "ink";
var SubmitButton = (props) => {
  const { isFocused } = useFocus2({ isActive: props.canSubmit });
  useInput15((input, key) => {
    if (key.return && isFocused && props.canSubmit) {
      props.onSubmit();
    }
  });
  return React22.createElement(
    Box21,
    { marginRight: 2 },
    React22.createElement(
      Box21,
      { marginRight: 2, paddingY: 1 },
      React22.createElement(Text19, null, !props.canSubmit ? "There are still required inputs you have not completed yet." : isFocused ? "Press Enter to submit form" : "Use the arrow keys to navigate to the submit button.")
    ),
    React22.createElement(
      Box21,
      { borderStyle: "round", borderColor: !props.canSubmit ? "gray" : isFocused ? "blue" : "white", paddingX: 2 },
      React22.createElement(Text19, { color: !props.canSubmit ? "gray" : isFocused ? "blue" : "white", bold: true, underline: isFocused }, props.canSubmit ? "Submit form" : "Cannot submit form yet")
    )
  );
};

// node_modules/ink-form/lib/Form.js
var Form = (props) => {
  const isControlled = props.value !== void 0;
  const [currentTab, setCurrentTab] = useState18(0);
  const [value, setValue] = useState18(props.value ?? {});
  const [editingField, setEditingField] = useState18();
  const canSubmitForm = useMemo2(() => canSubmit(props.form, value), [value, props.form]);
  const focusManager = useFocusManager();
  useEffect19(() => {
    focusManager.enableFocus();
  }, []);
  useEffect19(() => {
    if (props.value) {
      setValue(props.value);
    }
  }, [props.value]);
  useEffect19(() => {
    if (!isControlled) {
      setValueAndPropagate({
        ...value,
        ...props.form.sections.map((section) => section.fields.map((field) => field.initialValue !== void 0 ? { [field.name]: field.initialValue } : {}).reduce((obj1, obj2) => ({ ...obj1, ...obj2 }), {})).reduce((obj1, obj2) => ({ ...obj1, ...obj2 }), {})
      });
    }
  }, []);
  const setValueAndPropagate = (value2) => {
    setValue(value2);
    props.onChange?.(value2);
  };
  useInput16((input, key) => {
    if (key.upArrow) {
      focusManager.focusPrevious();
    } else if (key.downArrow) {
      focusManager.focusNext();
    }
  }, { isActive: !editingField });
  return React23.createElement(
    Box22,
    { width: "100%", height: "100%", flexDirection: "column" },
    React23.createElement(FormHeader, { ...props, currentTab, onChangeTab: setCurrentTab, editingField }),
    !editingField && props.form.sections[currentTab].description && React23.createElement(
      Box22,
      { marginX: 4 },
      React23.createElement(DescriptionRenderer, { description: props.form.sections[currentTab].description })
    ),
    React23.createElement(Box22, { flexDirection: "column" }, currentTab > props.form.sections.length - 1 ? null : props.form.sections[currentTab].fields.map((field) => React23.createElement(FormFieldRenderer, { field, key: field.name, form: props.form, value: value[field.name], onChange: (v) => setValueAndPropagate({ ...value, [field.name]: v }), onSetEditingField: setEditingField, editingField, customManagers: props.customManagers }))),
    !editingField && React23.createElement(
      Box22,
      { flexDirection: "row-reverse" },
      React23.createElement(SubmitButton, { canSubmit: canSubmitForm, onSubmit: () => props.onSubmit?.(value) })
    )
  );
};

// node_modules/ink-form/lib/imperativeInterface.js
import { render } from "ink";
import React24 from "react";

// src/utils/schemaToForm.ts
function toSelectOptions(values, names) {
  const useNames = names !== void 0 && names.length === values.length;
  return values.map((val, index) => ({
    label: useNames ? names[index] : String(val),
    value: String(val)
  }));
}
function schemaToForm(schema, toolName) {
  const fields = [];
  if (!schema || !schema.properties) {
    return {
      title: `Test Tool: ${toolName}`,
      sections: [{ title: "Parameters", fields: [] }]
    };
  }
  const properties = schema.properties || {};
  const required = schema.required || [];
  for (const [key, prop] of Object.entries(properties)) {
    const property = typeof prop === "object" && prop !== null ? prop : {};
    const baseField = {
      name: key,
      label: property.title || key,
      required: required.includes(key)
    };
    let field;
    if (property.type === "array" && property.items?.enum) {
      field = {
        type: "select",
        ...baseField,
        options: toSelectOptions(property.items.enum, property.items.enumNames)
      };
    } else if (property.enum) {
      field = {
        type: "select",
        ...baseField,
        options: toSelectOptions(property.enum, property.enumNames)
      };
    } else {
      switch (property.type) {
        case "string":
          field = {
            type: "string",
            ...baseField
          };
          break;
        case "integer":
          field = {
            type: "integer",
            ...baseField,
            ...property.minimum !== void 0 && { min: property.minimum },
            ...property.maximum !== void 0 && { max: property.maximum }
          };
          break;
        case "number":
          field = {
            type: "float",
            ...baseField,
            ...property.minimum !== void 0 && { min: property.minimum },
            ...property.maximum !== void 0 && { max: property.maximum }
          };
          break;
        case "boolean":
          field = {
            type: "boolean",
            ...baseField
          };
          break;
        default:
          field = {
            type: "string",
            ...baseField
          };
      }
    }
    if (property.default !== void 0) {
      field.initialValue = property.default;
    }
    fields.push(field);
  }
  const sections = [
    {
      title: "Parameters",
      fields
    }
  ];
  return {
    title: `Test Tool: ${toolName}`,
    sections
  };
}

// src/components/ToolTestModal.tsx
import { Fragment as Fragment8, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
function ToolTestModal({
  tool,
  inspectorClient,
  width,
  height,
  onClose,
  onAuthRecoveryRequired
}) {
  const [state, setState] = useState19("form");
  const [result, setResult] = useState19(null);
  const scrollViewRef = React25.useRef(null);
  const [terminalDimensions, setTerminalDimensions] = React25.useState({
    width: process.stdout.columns || width,
    height: process.stdout.rows || height
  });
  React25.useEffect(() => {
    const updateDimensions = () => {
      setTerminalDimensions({
        width: process.stdout.columns || width,
        height: process.stdout.rows || height
      });
    };
    process.stdout.on("resize", updateDimensions);
    updateDimensions();
    return () => {
      process.stdout.off("resize", updateDimensions);
    };
  }, [width, height]);
  const formStructure = tool?.inputSchema ? schemaToForm(tool.inputSchema, tool.name || "Unknown Tool") : {
    title: `Test Tool: ${tool?.name || "Unknown"}`,
    sections: [{ title: "Parameters", fields: [] }]
  };
  React25.useEffect(() => {
    return () => {
      setState("form");
      setResult(null);
    };
  }, []);
  useInput17(
    (input, key) => {
      if (key.escape) {
        setState("form");
        setResult(null);
        onClose();
        return;
      }
      if (state === "form") {
        return;
      }
      if (state === "results") {
        if (key.downArrow) {
          scrollViewRef.current?.scrollBy(1);
        } else if (key.upArrow) {
          scrollViewRef.current?.scrollBy(-1);
        } else if (key.pageDown) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(viewportHeight);
        } else if (key.pageUp) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(-viewportHeight);
        }
      }
    },
    { isActive: true }
  );
  const handleFormSubmit = async (values) => {
    if (!inspectorClient || !tool) return;
    setState("loading");
    const startTime = Date.now();
    try {
      const invocation = await inspectorClient.callTool(tool, values);
      const duration = Date.now() - startTime;
      if (!invocation.success || invocation.result === null) {
        setResult({
          input: values,
          output: null,
          error: invocation.error || "Tool call failed",
          errorDetails: invocation,
          duration
        });
      } else {
        const result2 = invocation.result;
        const isError = "isError" in result2 && result2.isError === true;
        setResult({
          input: values,
          output: isError ? null : result2,
          error: isError ? "Tool returned an error" : void 0,
          errorDetails: isError ? result2 : void 0,
          duration
        });
      }
      setState("results");
    } catch (error) {
      if (error instanceof AuthRecoveryRequiredError) {
        onAuthRecoveryRequired?.(error);
        onClose();
        return;
      }
      const duration = Date.now() - startTime;
      const errorObj = error instanceof Error ? { message: error.message, name: error.name, stack: error.stack } : { error: String(error) };
      setResult({
        input: values,
        output: null,
        error: error instanceof Error ? error.message : "Unknown error",
        errorDetails: errorObj,
        duration
      });
      setState("results");
    }
  };
  const modalWidth = terminalDimensions.width - 2;
  const modalHeight = terminalDimensions.height - 2;
  return /* @__PURE__ */ jsx12(
    Box23,
    {
      position: "absolute",
      width: terminalDimensions.width,
      height: terminalDimensions.height,
      flexDirection: "column",
      justifyContent: "center",
      alignItems: "center",
      children: /* @__PURE__ */ jsxs11(
        Box23,
        {
          width: modalWidth,
          height: modalHeight,
          borderStyle: "single",
          borderColor: "cyan",
          flexDirection: "column",
          paddingX: 1,
          paddingY: 1,
          backgroundColor: "black",
          children: [
            /* @__PURE__ */ jsxs11(Box23, { flexShrink: 0, marginBottom: 1, children: [
              /* @__PURE__ */ jsx12(Text20, { bold: true, color: "cyan", children: formStructure.title }),
              /* @__PURE__ */ jsx12(Text20, { children: " " }),
              /* @__PURE__ */ jsx12(Text20, { dimColor: true, children: "(Press ESC to close)" })
            ] }),
            /* @__PURE__ */ jsxs11(Box23, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: [
              state === "form" && /* @__PURE__ */ jsx12(Box23, { flexGrow: 1, width: "100%", children: /* @__PURE__ */ jsx12(
                Form,
                {
                  form: formStructure,
                  onSubmit: (value) => void handleFormSubmit(value)
                }
              ) }),
              state === "loading" && /* @__PURE__ */ jsx12(Box23, { flexGrow: 1, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx12(Text20, { color: "yellow", children: "Calling tool..." }) }),
              state === "results" && result && /* @__PURE__ */ jsx12(Box23, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: /* @__PURE__ */ jsxs11(ScrollView, { ref: scrollViewRef, children: [
                /* @__PURE__ */ jsx12(Box23, { marginBottom: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs11(Text20, { bold: true, color: "green", children: [
                  "Duration: ",
                  result.duration,
                  "ms"
                ] }) }),
                /* @__PURE__ */ jsxs11(Box23, { marginBottom: 1, flexShrink: 0, flexDirection: "column", children: [
                  /* @__PURE__ */ jsx12(Text20, { bold: true, color: "cyan", children: "Input:" }),
                  /* @__PURE__ */ jsx12(Box23, { paddingLeft: 2, children: /* @__PURE__ */ jsx12(Text20, { dimColor: true, children: JSON.stringify(result.input, null, 2) }) })
                ] }),
                result.error ? /* @__PURE__ */ jsxs11(Box23, { flexShrink: 0, flexDirection: "column", children: [
                  /* @__PURE__ */ jsx12(Text20, { bold: true, color: "red", children: "Error:" }),
                  /* @__PURE__ */ jsx12(Box23, { paddingLeft: 2, children: /* @__PURE__ */ jsx12(Text20, { color: "red", children: String(result.error) }) }),
                  result.errorDetails != null ? /* @__PURE__ */ jsxs11(Fragment8, { children: [
                    /* @__PURE__ */ jsx12(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text20, { bold: true, color: "red", dimColor: true, children: "Error Details:" }) }),
                    /* @__PURE__ */ jsx12(Box23, { paddingLeft: 2, children: /* @__PURE__ */ jsx12(Text20, { dimColor: true, children: JSON.stringify(result.errorDetails, null, 2) }) })
                  ] }) : null
                ] }) : /* @__PURE__ */ jsxs11(Box23, { flexShrink: 0, flexDirection: "column", children: [
                  /* @__PURE__ */ jsx12(Text20, { bold: true, color: "green", children: "Output:" }),
                  /* @__PURE__ */ jsx12(Box23, { paddingLeft: 2, children: /* @__PURE__ */ jsx12(Text20, { dimColor: true, children: JSON.stringify(result.output, null, 2) }) })
                ] })
              ] }) })
            ] })
          ]
        }
      )
    }
  );
}

// src/components/ResourceTestModal.tsx
import React26, { useState as useState20 } from "react";
import { Box as Box24, Text as Text21, useInput as useInput18 } from "ink";

// src/utils/uriTemplateToForm.ts
import { UriTemplate as UriTemplate2 } from "@modelcontextprotocol/client";
function uriTemplateToForm(uriTemplate, templateName) {
  const fields = [];
  try {
    const template = new UriTemplate2(uriTemplate);
    const variableNames = template.variableNames || [];
    for (const variableName of variableNames) {
      const field = {
        name: variableName,
        label: variableName,
        type: "string",
        required: false
        // URI template variables are typically optional
      };
      fields.push(field);
    }
  } catch (error) {
    console.error("Failed to parse URI template:", error);
  }
  const sections = [
    {
      title: "Template Variables",
      fields
    }
  ];
  return {
    title: `Read Resource: ${templateName}`,
    sections
  };
}

// src/components/ResourceTestModal.tsx
import { Fragment as Fragment9, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
function getErrorMessage(error) {
  if (error instanceof Error) {
    return error.message;
  }
  if (typeof error === "string") {
    return error;
  }
  if (error && typeof error === "object" && "message" in error) {
    return String(error.message);
  }
  return "Unknown error";
}
function ResourceTestModal({
  template,
  inspectorClient,
  width,
  height,
  onClose,
  onAuthRecoveryRequired
}) {
  const [state, setState] = useState20("form");
  const [result, setResult] = useState20(null);
  const scrollViewRef = React26.useRef(null);
  const [terminalDimensions, setTerminalDimensions] = React26.useState({
    width: process.stdout.columns || width,
    height: process.stdout.rows || height
  });
  React26.useEffect(() => {
    const updateDimensions = () => {
      setTerminalDimensions({
        width: process.stdout.columns || width,
        height: process.stdout.rows || height
      });
    };
    process.stdout.on("resize", updateDimensions);
    updateDimensions();
    return () => {
      process.stdout.off("resize", updateDimensions);
    };
  }, [width, height]);
  const formStructure = uriTemplateToForm(
    template.uriTemplate,
    template.name || "Unknown Template"
  );
  React26.useEffect(() => {
    return () => {
      setState("form");
      setResult(null);
    };
  }, []);
  useInput18(
    (input, key) => {
      if (key.escape) {
        setState("form");
        setResult(null);
        onClose();
        return;
      }
      if (state === "form") {
        return;
      }
      if (state === "results") {
        if (key.downArrow) {
          scrollViewRef.current?.scrollBy(1);
        } else if (key.upArrow) {
          scrollViewRef.current?.scrollBy(-1);
        } else if (key.pageDown) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(viewportHeight);
        } else if (key.pageUp) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(-viewportHeight);
        }
      }
    },
    { isActive: true }
  );
  const handleFormSubmit = async (values) => {
    if (!inspectorClient || !template) return;
    setState("loading");
    const startTime = Date.now();
    try {
      const invocation = await inspectorClient.readResourceFromTemplate(
        template.uriTemplate,
        values
      );
      const duration = Date.now() - startTime;
      setResult({
        input: values,
        output: invocation.result,
        // Extract the SDK result from the invocation
        duration,
        uri: invocation.expandedUri
        // Use expandedUri instead of uri
      });
      setState("results");
    } catch (error) {
      if (error instanceof AuthRecoveryRequiredError) {
        onAuthRecoveryRequired?.(error);
        onClose();
        return;
      }
      const duration = Date.now() - startTime;
      const errorMessage = getErrorMessage(error);
      let uri = template.uriTemplate;
      if (error && typeof error === "object" && "uri" in error) {
        uri = error.uri;
      }
      const errorObj = {
        message: errorMessage
      };
      if (error instanceof Error) {
        errorObj.name = error.name;
        errorObj.stack = error.stack;
      } else if (error && typeof error === "object") {
        Object.assign(errorObj, error);
      } else {
        errorObj.error = String(error);
      }
      setResult({
        input: values,
        output: null,
        error: errorMessage,
        errorDetails: errorObj,
        duration,
        uri
      });
      setState("results");
    }
  };
  const modalWidth = terminalDimensions.width - 2;
  const modalHeight = terminalDimensions.height - 2;
  return /* @__PURE__ */ jsx13(
    Box24,
    {
      position: "absolute",
      width: terminalDimensions.width,
      height: terminalDimensions.height,
      flexDirection: "column",
      justifyContent: "center",
      alignItems: "center",
      children: /* @__PURE__ */ jsxs12(
        Box24,
        {
          width: modalWidth,
          height: modalHeight,
          borderStyle: "single",
          borderColor: "cyan",
          flexDirection: "column",
          paddingX: 1,
          paddingY: 1,
          backgroundColor: "black",
          children: [
            /* @__PURE__ */ jsxs12(Box24, { flexShrink: 0, marginBottom: 1, children: [
              /* @__PURE__ */ jsx13(Text21, { bold: true, color: "cyan", children: formStructure.title }),
              /* @__PURE__ */ jsx13(Text21, { children: " " }),
              /* @__PURE__ */ jsx13(Text21, { dimColor: true, children: "(Press ESC to close)" })
            ] }),
            /* @__PURE__ */ jsxs12(Box24, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: [
              state === "form" && /* @__PURE__ */ jsxs12(Box24, { flexGrow: 1, flexDirection: "column", children: [
                template.description && /* @__PURE__ */ jsx13(Box24, { marginBottom: 1, flexShrink: 0, children: /* @__PURE__ */ jsx13(Text21, { dimColor: true, children: template.description }) }),
                /* @__PURE__ */ jsx13(
                  Form,
                  {
                    form: formStructure,
                    onSubmit: (values) => handleFormSubmit(values)
                  }
                )
              ] }),
              state === "loading" && /* @__PURE__ */ jsx13(Box24, { flexGrow: 1, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx13(Text21, { color: "yellow", children: "Reading resource..." }) }),
              state === "results" && result && /* @__PURE__ */ jsx13(Box24, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: /* @__PURE__ */ jsxs12(ScrollView, { ref: scrollViewRef, children: [
                /* @__PURE__ */ jsx13(Box24, { marginBottom: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs12(Text21, { bold: true, color: "green", children: [
                  "Duration: ",
                  result.duration,
                  "ms"
                ] }) }),
                /* @__PURE__ */ jsxs12(Box24, { marginBottom: 1, flexShrink: 0, children: [
                  /* @__PURE__ */ jsxs12(Text21, { bold: true, color: "cyan", children: [
                    "URI:",
                    " "
                  ] }),
                  /* @__PURE__ */ jsx13(Text21, { dimColor: true, children: result.uri })
                ] }),
                /* @__PURE__ */ jsxs12(Box24, { marginBottom: 1, flexShrink: 0, flexDirection: "column", children: [
                  /* @__PURE__ */ jsx13(Text21, { bold: true, color: "cyan", children: "Template Values:" }),
                  /* @__PURE__ */ jsx13(Box24, { paddingLeft: 2, children: /* @__PURE__ */ jsx13(Text21, { dimColor: true, children: JSON.stringify(result.input, null, 2) }) })
                ] }),
                result.error ? /* @__PURE__ */ jsxs12(Box24, { flexShrink: 0, flexDirection: "column", children: [
                  /* @__PURE__ */ jsx13(Box24, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx13(Text21, { bold: true, color: "red", children: "Error:" }) }),
                  /* @__PURE__ */ jsx13(Box24, { marginTop: 1, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx13(Text21, { color: "red", children: String(result.error) }) }),
                  result.errorDetails != null ? /* @__PURE__ */ jsxs12(Fragment9, { children: [
                    /* @__PURE__ */ jsx13(Box24, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx13(Text21, { bold: true, color: "red", dimColor: true, children: "Error Details:" }) }),
                    /* @__PURE__ */ jsx13(Box24, { marginTop: 1, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx13(Text21, { dimColor: true, children: JSON.stringify(result.errorDetails, null, 2) }) })
                  ] }) : null
                ] }) : /* @__PURE__ */ jsxs12(Box24, { flexShrink: 0, flexDirection: "column", children: [
                  /* @__PURE__ */ jsx13(Text21, { bold: true, color: "green", children: "Resource Content:" }),
                  /* @__PURE__ */ jsx13(Box24, { paddingLeft: 2, children: /* @__PURE__ */ jsx13(Text21, { dimColor: true, children: JSON.stringify(result.output, null, 2) }) })
                ] })
              ] }) })
            ] })
          ]
        }
      )
    }
  );
}

// src/components/PromptTestModal.tsx
import React27, { useState as useState21 } from "react";
import { Box as Box25, Text as Text22, useInput as useInput19 } from "ink";

// src/utils/promptArgsToForm.ts
function promptArgsToForm(promptArguments, promptName) {
  const fields = [];
  if (!promptArguments || promptArguments.length === 0) {
    return {
      title: `Get Prompt: ${promptName}`,
      sections: [{ title: "Parameters", fields: [] }]
    };
  }
  for (const arg of promptArguments) {
    const field = {
      name: arg.name,
      label: arg.name,
      type: "string",
      // Prompt arguments are always strings
      required: arg.required !== false,
      // Default to required unless explicitly false
      description: arg.description
    };
    fields.push(field);
  }
  const sections = [
    {
      title: "Prompt Arguments",
      fields
    }
  ];
  return {
    title: `Get Prompt: ${promptName}`,
    sections
  };
}

// src/components/PromptTestModal.tsx
import { Fragment as Fragment10, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
function getErrorMessage2(error) {
  if (error instanceof Error) {
    return error.message;
  }
  if (typeof error === "string") {
    return error;
  }
  if (error && typeof error === "object" && "message" in error) {
    return String(error.message);
  }
  return "Unknown error";
}
function PromptTestModal({
  prompt,
  inspectorClient,
  width,
  height,
  onClose,
  onAuthRecoveryRequired
}) {
  const [state, setState] = useState21("form");
  const [result, setResult] = useState21(null);
  const scrollViewRef = React27.useRef(null);
  const [terminalDimensions, setTerminalDimensions] = React27.useState({
    width: process.stdout.columns || width,
    height: process.stdout.rows || height
  });
  React27.useEffect(() => {
    const updateDimensions = () => {
      setTerminalDimensions({
        width: process.stdout.columns || width,
        height: process.stdout.rows || height
      });
    };
    process.stdout.on("resize", updateDimensions);
    updateDimensions();
    return () => {
      process.stdout.off("resize", updateDimensions);
    };
  }, [width, height]);
  const formStructure = promptArgsToForm(
    prompt.arguments || [],
    prompt.name || "Unknown Prompt"
  );
  React27.useEffect(() => {
    return () => {
      setState("form");
      setResult(null);
    };
  }, []);
  useInput19(
    (input, key) => {
      if (key.escape) {
        setState("form");
        setResult(null);
        onClose();
        return;
      }
      if (state === "form") {
        return;
      }
      if (state === "results") {
        if (key.downArrow) {
          scrollViewRef.current?.scrollBy(1);
        } else if (key.upArrow) {
          scrollViewRef.current?.scrollBy(-1);
        } else if (key.pageDown) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(viewportHeight);
        } else if (key.pageUp) {
          const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
          scrollViewRef.current?.scrollBy(-viewportHeight);
        }
      }
    },
    { isActive: true }
  );
  const handleFormSubmit = async (values) => {
    if (!inspectorClient || !prompt) return;
    setState("loading");
    const startTime = Date.now();
    try {
      const invocation = await inspectorClient.getPrompt(prompt.name, values);
      const duration = Date.now() - startTime;
      setResult({
        input: values,
        output: invocation.result,
        duration
      });
      setState("results");
    } catch (error) {
      if (error instanceof AuthRecoveryRequiredError) {
        onAuthRecoveryRequired?.(error);
        onClose();
        return;
      }
      const duration = Date.now() - startTime;
      const errorMessage = getErrorMessage2(error);
      const errorObj = {
        message: errorMessage
      };
      if (error instanceof Error) {
        errorObj.name = error.name;
        errorObj.stack = error.stack;
      } else if (error && typeof error === "object") {
        Object.assign(errorObj, error);
      } else {
        errorObj.error = String(error);
      }
      setResult({
        input: values,
        output: null,
        error: errorMessage,
        errorDetails: errorObj,
        duration
      });
      setState("results");
    }
  };
  const modalWidth = terminalDimensions.width - 2;
  const modalHeight = terminalDimensions.height - 2;
  return /* @__PURE__ */ jsx14(
    Box25,
    {
      position: "absolute",
      width: terminalDimensions.width,
      height: terminalDimensions.height,
      flexDirection: "column",
      justifyContent: "center",
      alignItems: "center",
      children: /* @__PURE__ */ jsxs13(
        Box25,
        {
          width: modalWidth,
          height: modalHeight,
          borderStyle: "single",
          borderColor: "cyan",
          flexDirection: "column",
          paddingX: 1,
          paddingY: 1,
          backgroundColor: "black",
          children: [
            /* @__PURE__ */ jsxs13(Box25, { flexShrink: 0, marginBottom: 1, children: [
              /* @__PURE__ */ jsx14(Text22, { bold: true, color: "cyan", children: formStructure.title }),
              /* @__PURE__ */ jsx14(Text22, { children: " " }),
              /* @__PURE__ */ jsx14(Text22, { dimColor: true, children: "(Press ESC to close)" })
            ] }),
            /* @__PURE__ */ jsxs13(Box25, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: [
              state === "form" && /* @__PURE__ */ jsxs13(Box25, { flexGrow: 1, flexDirection: "column", children: [
                prompt.description && /* @__PURE__ */ jsx14(Box25, { marginBottom: 1, flexShrink: 0, children: /* @__PURE__ */ jsx14(Text22, { dimColor: true, children: prompt.description }) }),
                /* @__PURE__ */ jsx14(
                  Form,
                  {
                    form: formStructure,
                    onSubmit: (values) => handleFormSubmit(values)
                  }
                )
              ] }),
              state === "loading" && /* @__PURE__ */ jsx14(Box25, { flexGrow: 1, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx14(Text22, { color: "yellow", children: "Getting prompt..." }) }),
              state === "results" && result && /* @__PURE__ */ jsx14(Box25, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: /* @__PURE__ */ jsxs13(ScrollView, { ref: scrollViewRef, children: [
                /* @__PURE__ */ jsx14(Box25, { marginBottom: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs13(Text22, { bold: true, color: "green", children: [
                  "Duration: ",
                  result.duration,
                  "ms"
                ] }) }),
                Object.keys(result.input).length > 0 && /* @__PURE__ */ jsxs13(Box25, { marginBottom: 1, flexShrink: 0, flexDirection: "column", children: [
                  /* @__PURE__ */ jsx14(Text22, { bold: true, color: "cyan", children: "Arguments:" }),
                  /* @__PURE__ */ jsx14(Box25, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text22, { dimColor: true, children: JSON.stringify(result.input, null, 2) }) })
                ] }),
                result.error ? /* @__PURE__ */ jsxs13(Box25, { flexShrink: 0, flexDirection: "column", children: [
                  /* @__PURE__ */ jsx14(Box25, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx14(Text22, { bold: true, color: "red", children: "Error:" }) }),
                  /* @__PURE__ */ jsx14(Box25, { marginTop: 1, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx14(Text22, { color: "red", children: String(result.error) }) }),
                  result.errorDetails != null ? /* @__PURE__ */ jsxs13(Fragment10, { children: [
                    /* @__PURE__ */ jsx14(Box25, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx14(Text22, { bold: true, color: "red", dimColor: true, children: "Error Details:" }) }),
                    /* @__PURE__ */ jsx14(Box25, { marginTop: 1, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx14(Text22, { dimColor: true, children: JSON.stringify(result.errorDetails, null, 2) }) })
                  ] }) : null
                ] }) : /* @__PURE__ */ jsxs13(Box25, { flexShrink: 0, flexDirection: "column", children: [
                  /* @__PURE__ */ jsx14(Text22, { bold: true, color: "green", children: "Prompt Messages:" }),
                  /* @__PURE__ */ jsx14(Box25, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text22, { dimColor: true, children: JSON.stringify(result.output, null, 2) }) })
                ] })
              ] }) })
            ] })
          ]
        }
      )
    }
  );
}

// src/components/DetailsModal.tsx
import React28, { useRef as useRef12 } from "react";
import { Box as Box26, Text as Text23, useInput as useInput20 } from "ink";
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
function DetailsModal({
  title,
  content,
  width,
  height,
  onClose
}) {
  const scrollViewRef = useRef12(null);
  const [terminalDimensions, setTerminalDimensions] = React28.useState({
    width: process.stdout.columns || width,
    height: process.stdout.rows || height
  });
  React28.useEffect(() => {
    const updateDimensions = () => {
      setTerminalDimensions({
        width: process.stdout.columns || width,
        height: process.stdout.rows || height
      });
    };
    process.stdout.on("resize", updateDimensions);
    updateDimensions();
    return () => {
      process.stdout.off("resize", updateDimensions);
    };
  }, [width, height]);
  useInput20(
    (input, key) => {
      if (key.escape) {
        onClose();
      } else if (key.downArrow) {
        scrollViewRef.current?.scrollBy(1);
      } else if (key.upArrow) {
        scrollViewRef.current?.scrollBy(-1);
      } else if (key.pageDown) {
        const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
        scrollViewRef.current?.scrollBy(viewportHeight);
      } else if (key.pageUp) {
        const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1;
        scrollViewRef.current?.scrollBy(-viewportHeight);
      }
    },
    { isActive: true }
  );
  const modalWidth = terminalDimensions.width - 2;
  const modalHeight = terminalDimensions.height - 2;
  return /* @__PURE__ */ jsx15(
    Box26,
    {
      position: "absolute",
      width: terminalDimensions.width,
      height: terminalDimensions.height,
      flexDirection: "column",
      justifyContent: "center",
      alignItems: "center",
      children: /* @__PURE__ */ jsxs14(
        Box26,
        {
          width: modalWidth,
          height: modalHeight,
          borderStyle: "single",
          borderColor: "cyan",
          flexDirection: "column",
          paddingX: 1,
          paddingY: 1,
          backgroundColor: "black",
          children: [
            /* @__PURE__ */ jsxs14(Box26, { flexShrink: 0, marginBottom: 1, children: [
              /* @__PURE__ */ jsx15(Text23, { bold: true, color: "cyan", children: title }),
              /* @__PURE__ */ jsx15(Text23, { children: " " }),
              /* @__PURE__ */ jsx15(Text23, { dimColor: true, children: "(Press ESC to close)" })
            ] }),
            /* @__PURE__ */ jsx15(Box26, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: /* @__PURE__ */ jsx15(ScrollView, { ref: scrollViewRef, children: content }) })
          ]
        }
      )
    }
  );
}

// src/App.tsx
import { Fragment as Fragment11, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
var APP_NAME = "MCP Inspector TUI";
var APP_DESCRIPTION = "Terminal User Interface for the Model Context Protocol Inspector";
var APP_VERSION = readInspectorVersion(import.meta.url);
var TUI_CLIENT_NAME = "inspector-tui";
function App({
  mcpServers,
  clientConfig,
  clientId,
  clientSecret,
  clientMetadataUrl,
  callbackUrlConfig
}) {
  const { exit } = useApp();
  useEffect20(() => {
    getTuiLogger().info(
      { serverNames: Object.keys(mcpServers) },
      "TUI started"
    );
  }, [mcpServers]);
  const [selectedServer, setSelectedServer] = useState23(null);
  const [activeTab, setActiveTab] = useState23("info");
  const [focus, setFocus] = useState23("serverList");
  const [tabCounts, setTabCounts] = useState23({});
  const [oauthStatus, setOauthStatus] = useState23("idle");
  const [oauthMessage, setOauthMessage] = useState23(null);
  const [oauthRevision, setOauthRevision] = useState23(0);
  const [pendingStepUp, setPendingStepUp] = useState23(null);
  const pendingStepUpRef = useRef13(pendingStepUp);
  useEffect20(() => {
    pendingStepUpRef.current = pendingStepUp;
  }, [pendingStepUp]);
  const [connectError, setConnectError] = useState23(null);
  const oauthInProgressRef = useRef13(false);
  const callbackServerRef = useRef13(null);
  const selectedServerRef = useRef13(null);
  const mcpServersRef = useRef13(mcpServers);
  const inspectorClientsRef = useRef13({});
  const [toolTestModal, setToolTestModal] = useState23(null);
  const [resourceTestModal, setResourceTestModal] = useState23(null);
  const [promptTestModal, setPromptTestModal] = useState23(null);
  const [detailsModal, setDetailsModal] = useState23(null);
  const [inspectorClients, setInspectorClients] = useState23({});
  const [managedToolsStates, setManagedToolsStates] = useState23({});
  const [managedResourcesStates, setManagedResourcesStates] = useState23({});
  const [managedResourceTemplatesStates, setManagedResourceTemplatesStates] = useState23({});
  const [managedPromptsStates, setManagedPromptsStates] = useState23({});
  const [messageLogStates, setMessageLogStates] = useState23({});
  const [fetchRequestLogStates, setFetchRequestLogStates] = useState23({});
  const [stderrLogStates, setStderrLogStates] = useState23({});
  const [dimensions, setDimensions] = useState23({
    width: process.stdout.columns || 80,
    height: process.stdout.rows || 24
  });
  useEffect20(() => {
    const updateDimensions = () => {
      setDimensions({
        width: process.stdout.columns || 80,
        height: process.stdout.rows || 24
      });
    };
    process.stdout.on("resize", updateDimensions);
    return () => {
      process.stdout.off("resize", updateDimensions);
    };
  }, []);
  const serverNames = Object.keys(mcpServers);
  const selectedServerEntry = selectedServer ? mcpServers[selectedServer] : null;
  const selectedServerConfig = selectedServerEntry?.config ?? null;
  const redirectUrlProvidersRef = useRef13({});
  useEffect20(() => {
    const newClients = {};
    const newManagers = {};
    const newManagedResourcesStates = {};
    const newManagedResourceTemplatesStates = {};
    const newManagedPromptsStates = {};
    const newMessageLogStates = {};
    const newFetchRequestLogStates = {};
    const newStderrLogStates = {};
    for (const serverName of serverNames) {
      if (!(serverName in inspectorClients)) {
        const { config: serverConfig, settings: savedSettings } = mcpServers[serverName];
        const environment = {
          transport: createTransportNode,
          logger: getTuiLogger()
        };
        const defaultMetadata = savedSettings?.metadata ? Object.fromEntries(
          savedSettings.metadata.filter((m) => m.key.trim() !== "").map((m) => [m.key, m.value])
        ) : void 0;
        const clientAuthOptions = buildRunnerClientAuthOptions(
          clientConfig,
          savedSettings,
          { clientId, clientSecret, clientMetadataUrl }
        );
        const opts = {
          environment,
          clientIdentity: { name: TUI_CLIENT_NAME, version: APP_VERSION },
          pipeStderr: true,
          ...savedSettings && savedSettings.requestTimeout > 0 && {
            timeout: savedSettings.requestTimeout
          },
          ...defaultMetadata && Object.keys(defaultMetadata).length > 0 && {
            defaultMetadata
          },
          ...savedSettings && { serverSettings: savedSettings },
          // Advertise the roots configured for this server in mcp.json, as web
          // and the CLI do. The TUI has no roots editor, but a user who set
          // roots in the web UI expects them to apply to the same server here
          // (#1797).
          roots: cleanRoots(savedSettings?.roots ?? []),
          // Per-server protocol era (SEP §7.8) from mcp.json → SDK
          // versionNegotiation; absent era defaults to legacy (#1626).
          ...savedSettings?.protocolEra && {
            versionNegotiation: eraToVersionNegotiation(
              savedSettings.protocolEra
            )
          },
          ...clientAuthOptions
        };
        if (isOAuthCapableServerConfig(serverConfig)) {
          const redirectUrlProvider = redirectUrlProvidersRef.current[serverName] ?? (redirectUrlProvidersRef.current[serverName] = new MutableRedirectUrlProvider());
          redirectUrlProvider.redirectUrl = formatRunnerOAuthRedirectUrl(callbackUrlConfig);
          environment.oauth = {
            storage: new NodeOAuthStorage(),
            navigation: new CallbackNavigation(
              async (url) => await openUrl(url)
            ),
            redirectUrlProvider
          };
        }
        const client = new InspectorClient(serverConfig, opts);
        newClients[serverName] = client;
        newManagers[serverName] = new ManagedToolsState(client);
        newManagedResourcesStates[serverName] = new ManagedResourcesState(
          client
        );
        newManagedResourceTemplatesStates[serverName] = new ManagedResourceTemplatesState(client);
        newManagedPromptsStates[serverName] = new ManagedPromptsState(client);
        newMessageLogStates[serverName] = new MessageLogState(client);
        newFetchRequestLogStates[serverName] = new FetchRequestLogState(client);
        newStderrLogStates[serverName] = new StderrLogState(client);
      }
    }
    if (Object.keys(newClients).length > 0) {
      setInspectorClients((prev) => ({ ...prev, ...newClients }));
      setManagedToolsStates((prev) => ({ ...prev, ...newManagers }));
      setManagedResourcesStates((prev) => ({
        ...prev,
        ...newManagedResourcesStates
      }));
      setManagedResourceTemplatesStates((prev) => ({
        ...prev,
        ...newManagedResourceTemplatesStates
      }));
      setManagedPromptsStates((prev) => ({
        ...prev,
        ...newManagedPromptsStates
      }));
      setMessageLogStates((prev) => ({ ...prev, ...newMessageLogStates }));
      setFetchRequestLogStates((prev) => ({
        ...prev,
        ...newFetchRequestLogStates
      }));
      setStderrLogStates((prev) => ({ ...prev, ...newStderrLogStates }));
    }
  }, [
    clientConfig,
    clientId,
    clientSecret,
    clientMetadataUrl,
    callbackUrlConfig
  ]);
  useEffect20(() => {
    return () => {
      void callbackServerRef.current?.stop();
      callbackServerRef.current = null;
      Object.values(managedToolsStates).forEach((manager) => {
        manager.destroy();
      });
      Object.values(managedResourcesStates).forEach((manager) => {
        manager.destroy();
      });
      Object.values(managedResourceTemplatesStates).forEach((manager) => {
        manager.destroy();
      });
      Object.values(managedPromptsStates).forEach((manager) => {
        manager.destroy();
      });
      Object.values(messageLogStates).forEach((manager) => {
        manager.destroy();
      });
      Object.values(fetchRequestLogStates).forEach((manager) => {
        manager.destroy();
      });
      Object.values(stderrLogStates).forEach((manager) => {
        manager.destroy();
      });
      Object.values(inspectorClients).forEach((client) => {
        client.disconnect().catch(() => {
        });
      });
    };
  }, [
    inspectorClients,
    managedToolsStates,
    managedResourcesStates,
    managedResourceTemplatesStates,
    managedPromptsStates,
    messageLogStates,
    fetchRequestLogStates,
    stderrLogStates
  ]);
  useEffect20(() => {
    if (serverNames.length > 0 && selectedServer === null) {
      setSelectedServer(serverNames[0]);
    }
  }, []);
  useEffect20(() => {
    setOauthStatus("idle");
    setOauthMessage(null);
    const stepUp = pendingStepUpRef.current;
    if (stepUp && selectedServer && stepUp.serverName !== selectedServer) {
      setPendingStepUp(null);
    }
  }, [selectedServer]);
  useEffect20(() => {
    selectedServerRef.current = selectedServer;
    mcpServersRef.current = mcpServers;
    inspectorClientsRef.current = inspectorClients;
  }, [selectedServer, mcpServers, inspectorClients]);
  useEffect20(() => {
    if (activeTab === "auth" && selectedServerConfig && !isOAuthCapableServerConfig(selectedServerConfig)) {
      setActiveTab("info");
    }
  }, [activeTab, selectedServerConfig]);
  const selectedInspectorClient = useMemo3(
    () => selectedServer ? inspectorClients[selectedServer] : null,
    [selectedServer, inspectorClients]
  );
  const {
    status: inspectorStatus,
    capabilities: inspectorCapabilities,
    serverInfo: inspectorServerInfo,
    instructions: inspectorInstructions,
    connect: connectInspector,
    disconnect: disconnectInspector,
    lastError: inspectorLastError
  } = useInspectorClient(selectedInspectorClient);
  const selectedMessageLogState = useMemo3(
    () => selectedServer && messageLogStates[selectedServer] ? messageLogStates[selectedServer] : null,
    [selectedServer, messageLogStates]
  );
  const selectedFetchRequestLogState = useMemo3(
    () => selectedServer && fetchRequestLogStates[selectedServer] ? fetchRequestLogStates[selectedServer] : null,
    [selectedServer, fetchRequestLogStates]
  );
  const selectedStderrLogState = useMemo3(
    () => selectedServer && stderrLogStates[selectedServer] ? stderrLogStates[selectedServer] : null,
    [selectedServer, stderrLogStates]
  );
  const { messages: inspectorMessages } = useMessageLog(
    selectedMessageLogState
  );
  const { fetchRequests: inspectorFetchRequests } = useFetchRequestLog(
    selectedFetchRequestLogState
  );
  const { stderrLogs: inspectorStderrLogs } = useStderrLog(
    selectedStderrLogState
  );
  const selectedManagedToolsState = useMemo3(
    () => selectedServer && managedToolsStates[selectedServer] ? managedToolsStates[selectedServer] : null,
    [selectedServer, managedToolsStates]
  );
  const { tools: managedTools } = useManagedTools(
    selectedInspectorClient,
    selectedManagedToolsState
  );
  const selectedManagedResourcesState = useMemo3(
    () => selectedServer && managedResourcesStates[selectedServer] ? managedResourcesStates[selectedServer] : null,
    [selectedServer, managedResourcesStates]
  );
  const selectedManagedResourceTemplatesState = useMemo3(
    () => selectedServer && managedResourceTemplatesStates[selectedServer] ? managedResourceTemplatesStates[selectedServer] : null,
    [selectedServer, managedResourceTemplatesStates]
  );
  const selectedManagedPromptsState = useMemo3(
    () => selectedServer && managedPromptsStates[selectedServer] ? managedPromptsStates[selectedServer] : null,
    [selectedServer, managedPromptsStates]
  );
  const { resources: managedResources } = useManagedResources(
    selectedInspectorClient,
    selectedManagedResourcesState
  );
  const { resourceTemplates: managedResourceTemplates } = useManagedResourceTemplates(
    selectedInspectorClient,
    selectedManagedResourceTemplatesState
  );
  const { prompts: managedPrompts } = useManagedPrompts(
    selectedInspectorClient,
    selectedManagedPromptsState
  );
  const runOAuthAuthentication = useCallback11(
    async (options) => {
      const serverName = options?.serverName ?? selectedServer;
      if (!serverName) {
        return "unsupported";
      }
      const client = inspectorClientsRef.current[serverName];
      const serverEntry = mcpServersRef.current[serverName];
      const serverConfig = serverEntry?.config;
      if (!client || !serverConfig || !isOAuthCapableServerConfig(serverConfig)) {
        return "unsupported";
      }
      if (oauthInProgressRef.current) {
        return "skipped";
      }
      oauthInProgressRef.current = true;
      getTuiLogger().info(
        { server: serverName },
        "OAuth authentication started"
      );
      const existing = callbackServerRef.current;
      if (existing) {
        await existing.stop();
        callbackServerRef.current = null;
      }
      const redirectUrlProvider = redirectUrlProvidersRef.current[serverName];
      if (!redirectUrlProvider) {
        oauthInProgressRef.current = false;
        return "unsupported";
      }
      try {
        const result = await runRunnerInteractiveOAuth({
          client,
          redirectUrlProvider,
          callbackListen: callbackUrlConfig,
          createCallbackServer: createOAuthCallbackServer,
          onCallbackServer: (server) => {
            callbackServerRef.current = server;
          },
          authorizationUrl: options?.authorizationUrl,
          authChallenge: options?.challenge
        });
        if (result.kind === "insufficient_scope") {
          setOauthStatus("error");
          setOauthMessage(stepUpInsufficientScopeMessage(result.challenge));
          return "insufficient_scope";
        }
        if (result.kind === "success" || result.kind === "already_authorized") {
          setOauthRevision((n) => n + 1);
          return result.kind;
        }
        return "unsupported";
      } finally {
        oauthInProgressRef.current = false;
        callbackServerRef.current = null;
      }
    },
    [selectedServer, callbackUrlConfig]
  );
  const presentStepUpForServer = useCallback11(
    (serverName, challenge, authorizationUrl, enterpriseManaged) => {
      const pending = pendingStepUpRef.current;
      if (pending && pending.serverName !== serverName) {
        setOauthMessage(
          "A step-up prompt is already open. Complete or decline it before continuing."
        );
        return;
      }
      if (pending?.serverName === serverName) {
        setOauthMessage(
          "Updated step-up request \u2014 review the scopes on the Auth tab."
        );
      } else {
        setOauthMessage(null);
      }
      setSelectedServer(serverName);
      setPendingStepUp({
        serverName,
        challenge,
        authorizationUrl,
        enterpriseManaged
      });
      setActiveTab("auth");
      setOauthStatus("idle");
      setFocus("tabContentList");
    },
    []
  );
  const handleAuthRecoveryRequired = useCallback11(
    (serverName, error) => {
      const serverEntry = mcpServersRef.current[serverName];
      const settings = serverEntry?.settings;
      const client = inspectorClientsRef.current[serverName];
      const needsStepUpConfirm = error.emaStepUpConfirm || isStepUpConfirmation2(error.authChallenge, settings);
      if (needsStepUpConfirm) {
        void (async () => {
          if (client && await client.checkAuthChallengeSatisfied(error.authChallenge)) {
            setOauthStatus("idle");
            setOauthMessage("Authorization updated. Retry your action.");
            setOauthRevision((n) => n + 1);
            return;
          }
          presentStepUpForServer(
            serverName,
            error.authChallenge,
            error.authorizationUrl,
            settings?.enterpriseManaged
          );
        })();
        return;
      }
      void (async () => {
        if (client && await client.checkAuthChallengeSatisfied(error.authChallenge)) {
          setOauthStatus("idle");
          setOauthMessage("Authorization updated. Retry your action.");
          setOauthRevision((n) => n + 1);
          return;
        }
        const needsSwitch = selectedServerRef.current !== serverName;
        if (needsSwitch) {
          setSelectedServer(serverName);
          setActiveTab("auth");
          setOauthMessage(
            `Authentication required for "${serverName}". Re-authenticating\u2026`
          );
        } else {
          setOauthMessage(null);
        }
        setOauthStatus("authenticating");
        try {
          const oauthResult = await runOAuthAuthentication({
            challenge: error.authChallenge,
            authorizationUrl: error.authorizationUrl,
            serverName
          });
          if (oauthResult === "success" || oauthResult === "already_authorized") {
            setOauthStatus("idle");
            setOauthMessage("Authorization updated. Retry your action.");
          } else if (oauthResult === "skipped") {
            setOauthStatus("idle");
            setOauthMessage("OAuth already in progress.");
          }
        } catch (authErr) {
          const authMsg = authErr instanceof Error ? authErr.message : String(authErr);
          setOauthStatus("error");
          setOauthMessage(authMsg);
        }
      })();
    },
    [presentStepUpForServer, runOAuthAuthentication]
  );
  const onAuthRecoveryRequired = useCallback11(
    (error) => {
      if (selectedServer) {
        handleAuthRecoveryRequired(selectedServer, error);
      }
    },
    [selectedServer, handleAuthRecoveryRequired]
  );
  const handleConnect = useCallback11(async () => {
    if (!selectedServer || !selectedInspectorClient || !selectedServerConfig) {
      return;
    }
    const finishConnect = async () => {
      await connectInspector();
      setConnectError(null);
      setOauthStatus("idle");
      setOauthMessage(null);
      setOauthRevision((n) => n + 1);
    };
    try {
      await finishConnect();
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      setConnectError(msg);
      if (isEmaClientNotConfiguredError(err)) {
        setOauthStatus("error");
        setOauthMessage(err.message);
        return;
      }
      if (isOAuthCapableServerConfig(selectedServerConfig) && isUnauthorizedError(err)) {
        try {
          setOauthStatus("authenticating");
          setOauthMessage(null);
          await disconnectInspector();
          const oauthResult = await runOAuthAuthentication();
          if (oauthResult === "success" || oauthResult === "already_authorized") {
            await finishConnect();
          } else if (oauthResult === "skipped") {
            setOauthStatus("idle");
            setOauthMessage("OAuth already in progress.");
          }
        } catch (authErr) {
          if (authErr instanceof AuthRecoveryRequiredError) {
            handleAuthRecoveryRequired(selectedServer, authErr);
            return;
          }
          const authMsg = authErr instanceof Error ? authErr.message : String(authErr);
          setConnectError(authMsg);
          if (isEmaClientNotConfiguredError(authErr)) {
            setOauthStatus("error");
            setOauthMessage(authErr.message);
            return;
          }
          setOauthStatus("error");
          setOauthMessage(authMsg);
        }
        return;
      }
      if (err instanceof AuthRecoveryRequiredError && selectedServer) {
        handleAuthRecoveryRequired(selectedServer, err);
        return;
      }
    }
  }, [
    selectedServer,
    selectedInspectorClient,
    selectedServerConfig,
    connectInspector,
    disconnectInspector,
    runOAuthAuthentication,
    handleAuthRecoveryRequired
  ]);
  useEffect20(() => {
    const cleanups = [];
    for (const [serverName, client] of Object.entries(inspectorClients)) {
      const onAmbient = () => {
        if (selectedServerRef.current !== serverName) return;
        setOauthStatus("idle");
        setOauthMessage("Refreshing authorization\u2026");
      };
      const onRecovered = () => {
        if (selectedServerRef.current !== serverName) return;
        setOauthMessage(null);
        setOauthRevision((n) => n + 1);
      };
      const onInteractive = (event) => {
        handleAuthRecoveryRequired(
          serverName,
          new AuthRecoveryRequiredError(
            event.detail.authorizationUrl,
            event.detail.challenge
          )
        );
      };
      const onOAuthError = (event) => {
        if (selectedServerRef.current !== serverName) return;
        const message = event.detail.error instanceof Error ? event.detail.error.message : String(event.detail.error);
        setOauthStatus("error");
        setOauthMessage(message);
      };
      client.addEventListener("authChallengeAmbient", onAmbient);
      client.addEventListener("authChallengeRecovered", onRecovered);
      client.addEventListener("authChallengeInteractive", onInteractive);
      client.addEventListener("oauthError", onOAuthError);
      cleanups.push(() => {
        client.removeEventListener("authChallengeAmbient", onAmbient);
        client.removeEventListener("authChallengeRecovered", onRecovered);
        client.removeEventListener("authChallengeInteractive", onInteractive);
        client.removeEventListener("oauthError", onOAuthError);
      });
    }
    return () => {
      for (const cleanup of cleanups) {
        cleanup();
      }
    };
  }, [inspectorClients, handleAuthRecoveryRequired]);
  const handleDisconnect = useCallback11(async () => {
    if (!selectedServer) return;
    await disconnectInspector();
  }, [selectedServer, disconnectInspector]);
  const handleClearOAuth = useCallback11(async () => {
    if (!selectedInspectorClient) return;
    await selectedInspectorClient.clearOAuthTokens();
    setOauthStatus("idle");
    setOauthMessage(null);
    setConnectError(null);
    if (inspectorStatus === "connected" || inspectorStatus === "connecting") {
      await disconnectInspector();
    }
    setOauthRevision((n) => n + 1);
  }, [selectedInspectorClient, inspectorStatus, disconnectInspector]);
  const currentServerState = useMemo3(() => {
    if (!selectedServer) return null;
    return {
      status: inspectorStatus,
      error: connectError ?? inspectorLastError ?? null,
      capabilities: inspectorCapabilities,
      serverInfo: inspectorServerInfo,
      instructions: inspectorInstructions,
      resources: managedResources,
      resourceTemplates: managedResourceTemplates,
      prompts: managedPrompts,
      tools: managedTools,
      stderrLogs: inspectorStderrLogs
      // InspectorClient manages this
    };
  }, [
    selectedServer,
    inspectorStatus,
    connectError,
    inspectorLastError,
    inspectorCapabilities,
    inspectorServerInfo,
    inspectorInstructions,
    managedResources,
    managedResourceTemplates,
    managedPrompts,
    managedTools,
    inspectorStderrLogs
  ]);
  const renderResourceDetails = (resource) => /* @__PURE__ */ jsxs15(Fragment11, { children: [
    "uri" in resource && resource.description && /* @__PURE__ */ jsx16(Fragment11, { children: resource.description.split("\n").map((line, idx) => /* @__PURE__ */ jsx16(
      Box27,
      {
        marginTop: idx === 0 ? 0 : 0,
        flexShrink: 0,
        children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: line })
      },
      `desc-${idx}`
    )) }),
    "uri" in resource && resource.uri && /* @__PURE__ */ jsxs15(Box27, { marginTop: 1, flexShrink: 0, children: [
      /* @__PURE__ */ jsx16(Text24, { bold: true, children: "URI:" }),
      /* @__PURE__ */ jsx16(Box27, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: resource.uri }) })
    ] }),
    "mimeType" in resource && resource.mimeType && /* @__PURE__ */ jsxs15(Box27, { marginTop: 1, flexShrink: 0, children: [
      /* @__PURE__ */ jsx16(Text24, { bold: true, children: "MIME Type:" }),
      /* @__PURE__ */ jsx16(Box27, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: resource.mimeType }) })
    ] }),
    /* @__PURE__ */ jsxs15(Box27, { marginTop: 1, flexShrink: 0, flexDirection: "column", children: [
      /* @__PURE__ */ jsx16(Text24, { bold: true, children: "Full JSON:" }),
      /* @__PURE__ */ jsx16(Box27, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: JSON.stringify(resource, null, 2) }) })
    ] })
  ] });
  const renderPromptDetails = (prompt) => /* @__PURE__ */ jsxs15(Fragment11, { children: [
    prompt.description && /* @__PURE__ */ jsx16(Fragment11, { children: prompt.description.split("\n").map((line, idx) => /* @__PURE__ */ jsx16(
      Box27,
      {
        marginTop: idx === 0 ? 0 : 0,
        flexShrink: 0,
        children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: line })
      },
      `desc-${idx}`
    )) }),
    prompt.arguments && prompt.arguments.length > 0 && /* @__PURE__ */ jsxs15(Fragment11, { children: [
      /* @__PURE__ */ jsx16(Box27, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx16(Text24, { bold: true, children: "Arguments:" }) }),
      prompt.arguments.map((arg, idx) => /* @__PURE__ */ jsx16(
        Box27,
        {
          marginTop: 1,
          paddingLeft: 2,
          flexShrink: 0,
          children: /* @__PURE__ */ jsxs15(Text24, { dimColor: true, children: [
            "- ",
            arg.name,
            ":",
            " ",
            arg.description ?? arg.type ?? "string"
          ] })
        },
        `arg-${idx}`
      ))
    ] }),
    /* @__PURE__ */ jsxs15(Box27, { marginTop: 1, flexShrink: 0, flexDirection: "column", children: [
      /* @__PURE__ */ jsx16(Text24, { bold: true, children: "Full JSON:" }),
      /* @__PURE__ */ jsx16(Box27, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: JSON.stringify(prompt, null, 2) }) })
    ] })
  ] });
  const renderToolDetails = (tool) => /* @__PURE__ */ jsxs15(Fragment11, { children: [
    tool.description && /* @__PURE__ */ jsx16(Fragment11, { children: tool.description.split("\n").map((line, idx) => /* @__PURE__ */ jsx16(
      Box27,
      {
        marginTop: idx === 0 ? 0 : 0,
        flexShrink: 0,
        children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: line })
      },
      `desc-${idx}`
    )) }),
    tool.inputSchema && /* @__PURE__ */ jsxs15(Box27, { marginTop: 1, flexShrink: 0, flexDirection: "column", children: [
      /* @__PURE__ */ jsx16(Text24, { bold: true, children: "Input Schema:" }),
      /* @__PURE__ */ jsx16(Box27, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: JSON.stringify(tool.inputSchema, null, 2) }) })
    ] }),
    /* @__PURE__ */ jsxs15(Box27, { marginTop: 1, flexShrink: 0, flexDirection: "column", children: [
      /* @__PURE__ */ jsx16(Text24, { bold: true, children: "Full JSON:" }),
      /* @__PURE__ */ jsx16(Box27, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: JSON.stringify(tool, null, 2) }) })
    ] })
  ] });
  const renderRequestDetails = (request) => /* @__PURE__ */ jsxs15(Fragment11, { children: [
    /* @__PURE__ */ jsx16(Box27, { flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text24, { bold: true, children: [
      request.method,
      " ",
      request.url
    ] }) }),
    /* @__PURE__ */ jsx16(Box27, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text24, { bold: true, children: [
      "Category:",
      " ",
      /* @__PURE__ */ jsx16(Text24, { children: request.category === "auth" ? "auth" : "transport" })
    ] }) }),
    request.responseStatus !== void 0 ? /* @__PURE__ */ jsx16(Box27, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text24, { bold: true, children: [
      "Status: ",
      request.responseStatus,
      " ",
      request.responseStatusText || ""
    ] }) }) : request.error ? /* @__PURE__ */ jsx16(Box27, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text24, { bold: true, color: "red", children: [
      "Error: ",
      request.error
    ] }) }) : null,
    request.duration !== void 0 && /* @__PURE__ */ jsx16(Box27, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text24, { dimColor: true, children: [
      request.timestamp.toLocaleTimeString(),
      " (",
      request.duration,
      "ms)"
    ] }) }),
    /* @__PURE__ */ jsxs15(Box27, { marginTop: 1, flexShrink: 0, children: [
      /* @__PURE__ */ jsx16(Text24, { bold: true, children: "Request Headers:" }),
      Object.entries(request.requestHeaders).map(([key, value]) => /* @__PURE__ */ jsx16(Box27, { marginTop: 0, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text24, { dimColor: true, children: [
        key,
        ": ",
        value
      ] }) }, key))
    ] }),
    request.requestBody && /* @__PURE__ */ jsxs15(Fragment11, { children: [
      /* @__PURE__ */ jsx16(Box27, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx16(Text24, { bold: true, children: "Request Body:" }) }),
      (() => {
        try {
          const parsed = JSON.parse(request.requestBody);
          return JSON.stringify(parsed, null, 2).split("\n").map((line, idx) => /* @__PURE__ */ jsx16(
            Box27,
            {
              marginTop: idx === 0 ? 1 : 0,
              paddingLeft: 2,
              flexShrink: 0,
              children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: line })
            },
            `req-body-${idx}`
          ));
        } catch {
          return /* @__PURE__ */ jsx16(Box27, { marginTop: 1, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: request.requestBody }) });
        }
      })()
    ] }),
    request.responseHeaders && Object.keys(request.responseHeaders).length > 0 && /* @__PURE__ */ jsxs15(Fragment11, { children: [
      /* @__PURE__ */ jsx16(Box27, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx16(Text24, { bold: true, children: "Response Headers:" }) }),
      Object.entries(request.responseHeaders).map(([key, value]) => /* @__PURE__ */ jsx16(Box27, { marginTop: 0, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text24, { dimColor: true, children: [
        key,
        ": ",
        value
      ] }) }, key))
    ] }),
    request.responseBody && /* @__PURE__ */ jsxs15(Fragment11, { children: [
      /* @__PURE__ */ jsx16(Box27, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx16(Text24, { bold: true, children: "Response Body:" }) }),
      (() => {
        try {
          const parsed = JSON.parse(request.responseBody);
          return JSON.stringify(parsed, null, 2).split("\n").map((line, idx) => /* @__PURE__ */ jsx16(
            Box27,
            {
              marginTop: idx === 0 ? 1 : 0,
              paddingLeft: 2,
              flexShrink: 0,
              children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: line })
            },
            `resp-body-${idx}`
          ));
        } catch {
          return /* @__PURE__ */ jsx16(Box27, { marginTop: 1, paddingLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: request.responseBody }) });
        }
      })()
    ] })
  ] });
  const renderMessageDetails = (message) => /* @__PURE__ */ jsxs15(Fragment11, { children: [
    /* @__PURE__ */ jsx16(Box27, { flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text24, { bold: true, children: [
      "Direction: ",
      message.direction
    ] }) }),
    /* @__PURE__ */ jsx16(Box27, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text24, { dimColor: true, children: [
      message.timestamp.toLocaleTimeString(),
      message.duration !== void 0 && ` (${message.duration}ms)`
    ] }) }),
    message.direction === "request" ? /* @__PURE__ */ jsxs15(Fragment11, { children: [
      /* @__PURE__ */ jsxs15(Box27, { marginTop: 1, flexShrink: 0, flexDirection: "column", children: [
        /* @__PURE__ */ jsx16(Text24, { bold: true, children: "Request:" }),
        /* @__PURE__ */ jsx16(Box27, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: JSON.stringify(message.message, null, 2) }) })
      ] }),
      message.response && /* @__PURE__ */ jsxs15(Box27, { marginTop: 1, flexShrink: 0, flexDirection: "column", children: [
        /* @__PURE__ */ jsx16(Text24, { bold: true, children: "Response:" }),
        /* @__PURE__ */ jsx16(Box27, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: JSON.stringify(message.response, null, 2) }) })
      ] })
    ] }) : /* @__PURE__ */ jsxs15(Box27, { marginTop: 1, flexShrink: 0, flexDirection: "column", children: [
      /* @__PURE__ */ jsx16(Text24, { bold: true, children: message.direction === "response" ? "Response:" : "Notification:" }),
      /* @__PURE__ */ jsx16(Box27, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: JSON.stringify(message.message, null, 2) }) })
    ] })
  ] });
  useEffect20(() => {
    if (!selectedServer) {
      return;
    }
    setTabCounts({
      resources: managedResources.length || 0,
      prompts: managedPrompts.length || 0,
      tools: managedTools.length || 0,
      messages: inspectorMessages.length || 0,
      requests: inspectorFetchRequests.length || 0,
      logging: inspectorStderrLogs.length || 0
    });
  }, [
    selectedServer,
    managedResources,
    managedPrompts,
    managedTools,
    inspectorMessages,
    inspectorFetchRequests,
    inspectorStderrLogs
  ]);
  useEffect20(() => {
    if (activeTab === "messages") {
      if (focus === "tabContentList" || focus === "tabContentDetails") {
        setFocus("messagesList");
      }
    } else if (activeTab === "requests") {
      if (focus === "tabContentList" || focus === "tabContentDetails") {
        setFocus("requestsList");
      }
    } else {
      if (focus === "messagesList" || focus === "messagesDetail" || focus === "requestsList" || focus === "requestsDetail") {
        setFocus("tabContentList");
      }
    }
  }, [activeTab]);
  useEffect20(() => {
    if (activeTab === "logging" && selectedServer) {
      const client = inspectorClients[selectedServer];
      if (client && client.getServerType() !== "stdio") {
        setActiveTab("info");
      }
    }
  }, [selectedServer, activeTab, inspectorClients]);
  useInput21((input, key) => {
    if (toolTestModal || resourceTestModal || promptTestModal || detailsModal) {
      return;
    }
    if (key.ctrl && input === "c") {
      exit();
    }
    if (key.escape) {
      exit();
    }
    const showAuthTab = !!selectedServer && !!selectedServerConfig && isOAuthCapableServerConfig(selectedServerConfig);
    const showLoggingTab = !!selectedServer && inspectorClients[selectedServer]?.getServerType() === "stdio";
    const showRequestsTab = !!selectedServer && (inspectorClients[selectedServer]?.getServerType() === "sse" || inspectorClients[selectedServer]?.getServerType() === "streamable-http");
    const tabAccelerators = Object.fromEntries(
      tabs.filter((tab) => {
        if (tab.id === "auth" && !showAuthTab) return false;
        if (tab.id === "logging" && !showLoggingTab) return false;
        if (tab.id === "requests" && !showRequestsTab) return false;
        return true;
      }).map((tab) => [
        tab.accelerator,
        tab.id
      ])
    );
    if (tabAccelerators[input.toLowerCase()]) {
      const nextTab = tabAccelerators[input.toLowerCase()];
      const authStepUpAccelerator = input.toLowerCase() === "a" && nextTab === "auth" && activeTab === "auth" && pendingStepUp?.serverName === selectedServer;
      if (!authStepUpAccelerator) {
        setActiveTab(nextTab);
        setFocus(nextTab === "auth" ? "tabContentList" : "tabs");
      }
    } else if (key.tab && !key.shift) {
      const focusOrder = activeTab === "messages" ? ["serverList", "tabs", "messagesList", "messagesDetail"] : activeTab === "requests" ? ["serverList", "tabs", "requestsList", "requestsDetail"] : ["serverList", "tabs", "tabContentList", "tabContentDetails"];
      const currentIndex = focusOrder.indexOf(focus);
      const nextIndex = (currentIndex + 1) % focusOrder.length;
      setFocus(focusOrder[nextIndex]);
    } else if (key.tab && key.shift) {
      const focusOrder = activeTab === "messages" ? ["serverList", "tabs", "messagesList", "messagesDetail"] : activeTab === "requests" ? ["serverList", "tabs", "requestsList", "requestsDetail"] : ["serverList", "tabs", "tabContentList", "tabContentDetails"];
      const currentIndex = focusOrder.indexOf(focus);
      const prevIndex = currentIndex > 0 ? currentIndex - 1 : focusOrder.length - 1;
      setFocus(focusOrder[prevIndex]);
    } else if (key.upArrow || key.downArrow) {
      if (focus === "serverList") {
        if (key.upArrow) {
          if (selectedServer === null) {
            setSelectedServer(serverNames[serverNames.length - 1] || null);
          } else {
            const currentIndex = serverNames.indexOf(selectedServer);
            const newIndex = currentIndex > 0 ? currentIndex - 1 : serverNames.length - 1;
            setSelectedServer(serverNames[newIndex] || null);
          }
        } else if (key.downArrow) {
          if (selectedServer === null) {
            setSelectedServer(serverNames[0] || null);
          } else {
            const currentIndex = serverNames.indexOf(selectedServer);
            const newIndex = currentIndex < serverNames.length - 1 ? currentIndex + 1 : 0;
            setSelectedServer(serverNames[newIndex] || null);
          }
        }
        return;
      }
    } else if (focus === "tabs" && (key.leftArrow || key.rightArrow)) {
      const showAuthTab2 = !!selectedServer && !!selectedServerConfig && isOAuthCapableServerConfig(selectedServerConfig);
      const showLoggingTab2 = !!selectedServer && inspectorClients[selectedServer]?.getServerType() === "stdio";
      const showRequestsTab2 = !!selectedServer && (inspectorClients[selectedServer]?.getServerType() === "sse" || inspectorClients[selectedServer]?.getServerType() === "streamable-http");
      const allTabs = [
        "info",
        "auth",
        "resources",
        "prompts",
        "tools",
        "messages",
        "requests",
        "logging"
      ];
      const tabs2 = allTabs.filter((t) => {
        if (t === "auth" && !showAuthTab2) return false;
        if (t === "logging" && !showLoggingTab2) return false;
        if (t === "requests" && !showRequestsTab2) return false;
        return true;
      });
      const currentIndex = tabs2.indexOf(activeTab);
      if (key.leftArrow) {
        const newIndex = currentIndex > 0 ? currentIndex - 1 : tabs2.length - 1;
        setActiveTab(tabs2[newIndex]);
      } else if (key.rightArrow) {
        const newIndex = currentIndex < tabs2.length - 1 ? currentIndex + 1 : 0;
        setActiveTab(tabs2[newIndex]);
      }
    }
    if (selectedServer) {
      if (input.toLowerCase() === "c" && (inspectorStatus === "disconnected" || inspectorStatus === "error")) {
        handleConnect();
      } else if (input.toLowerCase() === "d" && (inspectorStatus === "connected" || inspectorStatus === "connecting")) {
        handleDisconnect();
      }
    }
  });
  const headerHeight = 1;
  const tabsHeight = 1;
  const availableHeight = dimensions.height - headerHeight - tabsHeight;
  const serverDetailsMinHeight = 3;
  const contentHeight = availableHeight - serverDetailsMinHeight;
  const serverListWidth = Math.floor(dimensions.width * 0.3);
  const contentWidth = dimensions.width - serverListWidth;
  const getStatusColor = (status) => {
    switch (status) {
      case "connected":
        return "green";
      case "connecting":
        return "yellow";
      case "error":
        return "red";
      default:
        return "gray";
    }
  };
  const getStatusSymbol = (status) => {
    switch (status) {
      case "connected":
        return "\u25CF";
      case "connecting":
        return "\u25D0";
      case "error":
        return "\u2717";
      default:
        return "\u25CB";
    }
  };
  return /* @__PURE__ */ jsxs15(
    Box27,
    {
      flexDirection: "column",
      width: dimensions.width,
      height: dimensions.height,
      children: [
        /* @__PURE__ */ jsxs15(
          Box27,
          {
            width: dimensions.width,
            height: headerHeight,
            borderStyle: "single",
            borderTop: false,
            borderLeft: false,
            borderRight: false,
            paddingX: 1,
            justifyContent: "space-between",
            alignItems: "center",
            children: [
              /* @__PURE__ */ jsxs15(Box27, { children: [
                /* @__PURE__ */ jsx16(Text24, { bold: true, color: "cyan", children: APP_NAME }),
                /* @__PURE__ */ jsxs15(Text24, { dimColor: true, children: [
                  " - ",
                  APP_DESCRIPTION
                ] })
              ] }),
              /* @__PURE__ */ jsxs15(Text24, { dimColor: true, children: [
                "v",
                APP_VERSION
              ] })
            ]
          }
        ),
        /* @__PURE__ */ jsxs15(
          Box27,
          {
            flexDirection: "row",
            height: availableHeight + tabsHeight,
            width: dimensions.width,
            children: [
              /* @__PURE__ */ jsxs15(
                Box27,
                {
                  width: serverListWidth,
                  height: availableHeight + tabsHeight,
                  borderStyle: "single",
                  borderTop: false,
                  borderBottom: false,
                  borderLeft: false,
                  borderRight: true,
                  flexDirection: "column",
                  paddingX: 1,
                  children: [
                    /* @__PURE__ */ jsx16(Box27, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx16(
                      Text24,
                      {
                        bold: true,
                        backgroundColor: focus === "serverList" ? "yellow" : void 0,
                        children: "MCP Servers"
                      }
                    ) }),
                    /* @__PURE__ */ jsx16(Box27, { flexDirection: "column", flexGrow: 1, children: serverNames.map((serverName) => {
                      const isSelected = selectedServer === serverName;
                      return /* @__PURE__ */ jsx16(Box27, { paddingY: 0, children: /* @__PURE__ */ jsxs15(Text24, { children: [
                        isSelected ? "\u25B6 " : "  ",
                        serverName
                      ] }) }, serverName);
                    }) }),
                    /* @__PURE__ */ jsx16(
                      Box27,
                      {
                        flexShrink: 0,
                        height: 1,
                        justifyContent: "center",
                        backgroundColor: "gray",
                        children: /* @__PURE__ */ jsx16(Text24, { bold: true, color: "white", children: "ESC to exit" })
                      }
                    )
                  ]
                }
              ),
              /* @__PURE__ */ jsxs15(
                Box27,
                {
                  flexGrow: 1,
                  height: availableHeight + tabsHeight,
                  flexDirection: "column",
                  children: [
                    /* @__PURE__ */ jsx16(
                      Box27,
                      {
                        width: contentWidth,
                        borderStyle: "single",
                        borderTop: false,
                        borderLeft: false,
                        borderRight: false,
                        borderBottom: true,
                        paddingX: 1,
                        paddingY: 1,
                        flexDirection: "column",
                        flexShrink: 0,
                        children: /* @__PURE__ */ jsxs15(Box27, { flexDirection: "column", children: [
                          /* @__PURE__ */ jsxs15(
                            Box27,
                            {
                              flexDirection: "row",
                              justifyContent: "space-between",
                              alignItems: "center",
                              children: [
                                /* @__PURE__ */ jsx16(Text24, { bold: true, color: "cyan", children: selectedServer }),
                                /* @__PURE__ */ jsx16(Box27, { flexDirection: "row", alignItems: "center", gap: 1, children: currentServerState && /* @__PURE__ */ jsxs15(Fragment11, { children: [
                                  /* @__PURE__ */ jsxs15(Text24, { color: getStatusColor(currentServerState.status), children: [
                                    getStatusSymbol(currentServerState.status),
                                    " ",
                                    currentServerState.status
                                  ] }),
                                  /* @__PURE__ */ jsx16(Text24, { children: " " }),
                                  (currentServerState?.status === "disconnected" || currentServerState?.status === "error") && /* @__PURE__ */ jsxs15(Text24, { color: "cyan", bold: true, children: [
                                    "[",
                                    /* @__PURE__ */ jsx16(Text24, { underline: true, children: "C" }),
                                    "onnect]"
                                  ] }),
                                  (currentServerState?.status === "connected" || currentServerState?.status === "connecting") && /* @__PURE__ */ jsxs15(Text24, { color: "red", bold: true, children: [
                                    "[",
                                    /* @__PURE__ */ jsx16(Text24, { underline: true, children: "D" }),
                                    "isconnect]"
                                  ] })
                                ] }) })
                              ]
                            }
                          ),
                          oauthStatus === "authenticating" && /* @__PURE__ */ jsx16(Box27, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text24, { dimColor: true, children: "OAuth: authenticating\u2026" }) }),
                          oauthStatus === "error" && oauthMessage && /* @__PURE__ */ jsx16(Box27, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text24, { color: "red", children: [
                            "OAuth: ",
                            oauthMessage
                          ] }) })
                        ] })
                      }
                    ),
                    /* @__PURE__ */ jsx16(
                      Tabs,
                      {
                        activeTab,
                        onTabChange: setActiveTab,
                        width: contentWidth,
                        counts: tabCounts,
                        focused: focus === "tabs",
                        showAuth: !!(selectedServer && selectedServerConfig && isOAuthCapableServerConfig(selectedServerConfig)),
                        showLogging: selectedServer && inspectorClients[selectedServer] ? inspectorClients[selectedServer].getServerType() === "stdio" : false,
                        showRequests: selectedServer && inspectorClients[selectedServer] ? (() => {
                          const serverType = inspectorClients[selectedServer].getServerType();
                          return serverType === "sse" || serverType === "streamable-http";
                        })() : false
                      }
                    ),
                    /* @__PURE__ */ jsxs15(
                      Box27,
                      {
                        flexGrow: 1,
                        minHeight: 6,
                        width: contentWidth,
                        borderTop: false,
                        borderLeft: false,
                        borderRight: false,
                        borderBottom: false,
                        children: [
                          activeTab === "info" && /* @__PURE__ */ jsx16(
                            InfoTab,
                            {
                              serverName: selectedServer,
                              serverConfig: selectedServerConfig,
                              serverSettings: selectedServerEntry?.settings ?? null,
                              serverState: currentServerState,
                              width: contentWidth,
                              height: contentHeight,
                              focused: focus === "tabContentList" || focus === "tabContentDetails"
                            }
                          ),
                          activeTab === "auth" && selectedServer && selectedServerConfig && isOAuthCapableServerConfig(selectedServerConfig) ? /* @__PURE__ */ jsx16(
                            AuthTab,
                            {
                              serverName: selectedServer,
                              serverConfig: selectedServerConfig,
                              inspectorClient: selectedInspectorClient,
                              oauthStatus,
                              oauthMessage,
                              oauthRevision,
                              pendingStepUp: pendingStepUp?.serverName === selectedServer ? {
                                challenge: pendingStepUp.challenge,
                                authorizationScopes: pendingStepUp.challenge.authorizationScopes,
                                enterpriseManaged: pendingStepUp.enterpriseManaged
                              } : null,
                              onAuthorizeStepUp: () => {
                                if (!pendingStepUp || !selectedInspectorClient) return;
                                const { challenge, authorizationUrl, enterpriseManaged } = pendingStepUp;
                                setPendingStepUp(null);
                                setOauthStatus("authenticating");
                                void (async () => {
                                  try {
                                    if (enterpriseManaged) {
                                      const outcome = await selectedInspectorClient.handleAuthChallenge(
                                        challenge,
                                        { confirmedStepUp: true }
                                      );
                                      if (outcome.kind === "satisfied") {
                                        await disconnectInspector().catch(() => {
                                        });
                                        await connectInspector();
                                        setOauthStatus("idle");
                                        setOauthMessage(
                                          "Step-up authorization succeeded. Retry your action."
                                        );
                                        setOauthRevision((n) => n + 1);
                                        return;
                                      }
                                      if (outcome.kind === "interactive") {
                                        const oauthResult2 = await runOAuthAuthentication({
                                          challenge: outcome.challenge,
                                          authorizationUrl: outcome.authorizationUrl
                                        });
                                        if (oauthResult2 === "success" || oauthResult2 === "already_authorized") {
                                          setOauthStatus("idle");
                                          setOauthMessage(
                                            "Step-up authorization succeeded. Retry your action."
                                          );
                                        } else if (oauthResult2 === "skipped") {
                                          setOauthStatus("idle");
                                          setOauthMessage("OAuth already in progress.");
                                        }
                                        return;
                                      }
                                      if (outcome.kind === "failed") {
                                        setOauthStatus("error");
                                        setOauthMessage(
                                          emaStepUpFailureMessage(outcome.error.message)
                                        );
                                        return;
                                      }
                                      setOauthStatus("idle");
                                      setOauthMessage(
                                        "Step-up authorization succeeded. Retry your action."
                                      );
                                      return;
                                    }
                                    const oauthResult = await runOAuthAuthentication({
                                      challenge,
                                      authorizationUrl
                                    });
                                    if (oauthResult === "success" || oauthResult === "already_authorized") {
                                      setOauthStatus("idle");
                                      setOauthMessage(
                                        "Step-up authorization succeeded. Retry your action."
                                      );
                                    } else if (oauthResult === "skipped") {
                                      setOauthStatus("idle");
                                      setOauthMessage("OAuth already in progress.");
                                    }
                                  } catch (authErr) {
                                    const authMsg = authErr instanceof Error ? authErr.message : String(authErr);
                                    setOauthStatus("error");
                                    setOauthMessage(authMsg);
                                  }
                                })();
                              },
                              onCancelStepUp: () => {
                                setPendingStepUp(null);
                                setOauthMessage("Authorization cancelled.");
                              },
                              width: contentWidth,
                              height: contentHeight,
                              focused: focus === "tabContentList" || focus === "tabContentDetails",
                              onClearOAuth: () => {
                                void handleClearOAuth();
                              },
                              connectionStatus: inspectorStatus
                            }
                          ) : null,
                          activeTab === "resources" && currentServerState?.status === "connected" && selectedInspectorClient ? /* @__PURE__ */ jsx16(
                            ResourcesTab,
                            {
                              resources: currentServerState.resources,
                              resourceTemplates: currentServerState.resourceTemplates,
                              inspectorClient: selectedInspectorClient,
                              width: contentWidth,
                              height: contentHeight,
                              onCountChange: (count) => setTabCounts((prev) => ({ ...prev, resources: count })),
                              focusedPane: focus === "tabContentDetails" ? "details" : focus === "tabContentList" ? "list" : null,
                              onViewDetails: (resource) => setDetailsModal({
                                title: `Resource: ${"uri" in resource ? resource.name || resource.uri || "Unknown" : "Resource content"}`,
                                content: renderResourceDetails(resource)
                              }),
                              onFetchResource: () => {
                              },
                              onFetchTemplate: (template) => {
                                setResourceTestModal({
                                  template,
                                  inspectorClient: selectedInspectorClient
                                });
                              },
                              onAuthRecoveryRequired,
                              modalOpen: !!(toolTestModal || resourceTestModal || promptTestModal || detailsModal)
                            },
                            `resources-${selectedServer}`
                          ) : activeTab === "prompts" && currentServerState?.status === "connected" && selectedInspectorClient ? /* @__PURE__ */ jsx16(
                            PromptsTab,
                            {
                              prompts: currentServerState.prompts,
                              inspectorClient: selectedInspectorClient,
                              width: contentWidth,
                              height: contentHeight,
                              onCountChange: (count) => setTabCounts((prev) => ({ ...prev, prompts: count })),
                              focusedPane: focus === "tabContentDetails" ? "details" : focus === "tabContentList" ? "list" : null,
                              onViewDetails: (prompt) => setDetailsModal({
                                title: `Prompt: ${prompt.name || "Unknown"}`,
                                content: renderPromptDetails(prompt)
                              }),
                              onFetchPrompt: (prompt) => {
                                setPromptTestModal({
                                  prompt,
                                  inspectorClient: selectedInspectorClient
                                });
                              },
                              onAuthRecoveryRequired,
                              modalOpen: !!(toolTestModal || resourceTestModal || promptTestModal || detailsModal)
                            },
                            `prompts-${selectedServer}`
                          ) : activeTab === "tools" && currentServerState?.status === "connected" && selectedInspectorClient ? /* @__PURE__ */ jsx16(
                            ToolsTab,
                            {
                              tools: currentServerState.tools,
                              isConnected: inspectorStatus === "connected",
                              width: contentWidth,
                              height: contentHeight,
                              onCountChange: (count) => setTabCounts((prev) => ({ ...prev, tools: count })),
                              focusedPane: focus === "tabContentDetails" ? "details" : focus === "tabContentList" ? "list" : null,
                              onTestTool: (tool) => setToolTestModal({
                                tool,
                                inspectorClient: selectedInspectorClient
                              }),
                              onViewDetails: (tool) => setDetailsModal({
                                title: `Tool: ${tool.name || "Unknown"}`,
                                content: renderToolDetails(tool)
                              }),
                              modalOpen: !!(toolTestModal || detailsModal)
                            },
                            `tools-${selectedServer}`
                          ) : activeTab === "messages" && selectedInspectorClient ? /* @__PURE__ */ jsx16(
                            HistoryTab,
                            {
                              serverName: selectedServer,
                              messages: inspectorMessages,
                              width: contentWidth,
                              height: contentHeight,
                              onCountChange: (count) => setTabCounts((prev) => ({ ...prev, messages: count })),
                              focusedPane: focus === "messagesDetail" ? "details" : focus === "messagesList" ? "messages" : null,
                              modalOpen: !!(toolTestModal || detailsModal),
                              onViewDetails: (message) => {
                                const label = message.direction === "request" && "method" in message.message ? message.message.method : message.direction === "response" ? "Response" : message.direction === "notification" && "method" in message.message ? message.message.method : "Message";
                                setDetailsModal({
                                  title: `Message: ${label}`,
                                  content: renderMessageDetails(message)
                                });
                              }
                            }
                          ) : activeTab === "requests" && selectedInspectorClient && (inspectorStatus === "connected" || inspectorFetchRequests.length > 0) ? /* @__PURE__ */ jsx16(
                            RequestsTab,
                            {
                              serverName: selectedServer,
                              requests: inspectorFetchRequests,
                              width: contentWidth,
                              height: contentHeight,
                              onCountChange: (count) => setTabCounts((prev) => ({ ...prev, requests: count })),
                              focusedPane: focus === "requestsDetail" ? "details" : focus === "requestsList" ? "requests" : null,
                              modalOpen: !!(toolTestModal || detailsModal),
                              onViewDetails: (request) => {
                                setDetailsModal({
                                  title: `Request: ${request.method} ${request.url}`,
                                  content: renderRequestDetails(request)
                                });
                              }
                            }
                          ) : activeTab === "logging" && selectedInspectorClient ? /* @__PURE__ */ jsx16(
                            NotificationsTab,
                            {
                              stderrLogs: inspectorStderrLogs,
                              width: contentWidth,
                              height: contentHeight,
                              onCountChange: (count) => setTabCounts((prev) => ({ ...prev, logging: count })),
                              focused: focus === "tabContentList" || focus === "tabContentDetails"
                            }
                          ) : null
                        ]
                      }
                    )
                  ]
                }
              )
            ]
          }
        ),
        toolTestModal && /* @__PURE__ */ jsx16(
          ToolTestModal,
          {
            tool: toolTestModal.tool,
            inspectorClient: toolTestModal.inspectorClient,
            width: dimensions.width,
            height: dimensions.height,
            onClose: () => setToolTestModal(null),
            onAuthRecoveryRequired: (error) => {
              onAuthRecoveryRequired(error);
              setToolTestModal(null);
            }
          }
        ),
        resourceTestModal && /* @__PURE__ */ jsx16(
          ResourceTestModal,
          {
            template: resourceTestModal.template,
            inspectorClient: resourceTestModal.inspectorClient,
            width: dimensions.width,
            height: dimensions.height,
            onClose: () => setResourceTestModal(null),
            onAuthRecoveryRequired: (error) => {
              onAuthRecoveryRequired(error);
              setResourceTestModal(null);
            }
          }
        ),
        promptTestModal && /* @__PURE__ */ jsx16(
          PromptTestModal,
          {
            prompt: promptTestModal.prompt,
            inspectorClient: promptTestModal.inspectorClient,
            width: dimensions.width,
            height: dimensions.height,
            onClose: () => setPromptTestModal(null),
            onAuthRecoveryRequired: (error) => {
              onAuthRecoveryRequired(error);
              setPromptTestModal(null);
            }
          }
        ),
        detailsModal && /* @__PURE__ */ jsx16(
          DetailsModal,
          {
            title: detailsModal.title,
            content: detailsModal.content,
            width: dimensions.width,
            height: dimensions.height,
            onClose: () => setDetailsModal(null)
          }
        )
      ]
    }
  );
}
var App_default = App;

// tui.tsx
import { jsx as jsx17 } from "react/jsx-runtime";
async function runTui(args) {
  const program = new Command();
  program.name("mcp-inspector-tui").description("Terminal UI for MCP Inspector").option(
    "--catalog <path>",
    "Writable catalog file (created if missing; default: ~/.mcp-inspector/mcp.json, or MCP_CATALOG_PATH)"
  ).option(
    "--config <path>",
    "Read-only session config file (served as-is, never written or seeded; errors if absent)"
  ).option(
    "-e <key=value...>",
    "Environment variables for stdio servers",
    parseKeyValuePair,
    {}
  ).option("--cwd <path>", "Working directory for stdio servers").option(
    "--header <header...>",
    'HTTP headers as "Name: Value"',
    parseHeaderPair,
    {}
  ).option(
    "--client-id <id>",
    "OAuth client ID (static client) for HTTP servers"
  ).option(
    "--client-secret <secret>",
    "OAuth client secret (for confidential clients)"
  ).option(
    "--client-metadata-url <url>",
    "OAuth Client ID Metadata Document URL (CIMD) for HTTP servers"
  ).option(
    "--client-config <path>",
    "Install-level client config (default: ~/.mcp-inspector/storage/client.json, or MCP_CLIENT_CONFIG_PATH)"
  ).option(
    "--callback-url <url>",
    `OAuth redirect/callback listener URL; must be loopback (default: ${DEFAULT_RUNNER_OAUTH_CALLBACK_URL}, or MCP_OAUTH_CALLBACK_URL)`
  ).argument(
    "[target...]",
    "Command and args or URL for a single ad-hoc server (when not using --config)"
  ).option(
    "--transport <type>",
    "Transport: stdio, sse, or http (ad-hoc only)"
  ).option("--server-url <url>", "Server URL (ad-hoc only)").parse(args ?? process.argv);
  const options = program.opts();
  const targetArgs = program.args;
  const serverOptions = {
    catalogPath: options.catalog?.trim() || process.env.MCP_CATALOG_PATH,
    configPath: options.config?.trim() || void 0,
    target: targetArgs.length > 0 ? targetArgs : void 0,
    cwd: options.cwd?.trim() || void 0,
    env: options.e,
    headers: options.header,
    transport: options.transport,
    serverUrl: options.serverUrl?.trim() || void 0
  };
  const mcpServers = await loadServerEntries(serverOptions);
  const callbackUrlConfig = parseRunnerOAuthCallbackUrl(options.callbackUrl);
  const clientConfig = await loadRunnerClientConfig({
    clientConfigPath: options.clientConfig
  });
  const ansiEraseSavedLines = new RegExp(
    String.fromCharCode(27) + "\\[3J",
    "g"
  );
  const originalWrite = process.stdout.write.bind(process.stdout);
  process.stdout.write = function(chunk, encoding, cb) {
    if (typeof chunk === "string") {
      if (chunk.includes("\x1B[3J")) {
        chunk = chunk.replace(ansiEraseSavedLines, "");
      }
    } else if (Buffer.isBuffer(chunk)) {
      if (chunk.includes("\x1B[3J")) {
        let str = chunk.toString("utf8");
        str = str.replace(ansiEraseSavedLines, "");
        chunk = Buffer.from(str, "utf8");
      }
    }
    if (typeof encoding === "function") {
      return originalWrite(chunk, encoding);
    }
    return originalWrite(chunk, encoding, cb);
  };
  if (process.stdout.isTTY) {
    process.stdout.write("\x1B[?1049h");
  }
  const instance = render2(
    /* @__PURE__ */ jsx17(
      App_default,
      {
        mcpServers,
        clientConfig,
        clientId: options.clientId,
        clientSecret: options.clientSecret,
        clientMetadataUrl: options.clientMetadataUrl,
        callbackUrlConfig
      }
    )
  );
  try {
    await instance.waitUntilExit();
    if (process.stdout.isTTY) {
      process.stdout.write("\x1B[?1049l");
    }
    process.exit(0);
  } catch (error) {
    if (process.stdout.isTTY) {
      process.stdout.write("\x1B[?1049l");
    }
    console.error("TUI Error:", error);
    process.exit(1);
  }
}

// index.ts
var __filename = fileURLToPath2(import.meta.url);
var isMain = process.argv[1] !== void 0 && resolve3(process.argv[1]) === resolve3(__filename);
if (isMain) {
  runTui(process.argv).catch((err) => {
    console.error("Error:", err instanceof Error ? err.message : err);
    const debugOn = (v) => {
      const s = v?.trim().toLowerCase();
      return !!s && s !== "0" && s !== "false";
    };
    if ((debugOn(process.env.MCP_DEBUG) || debugOn(process.env.DEBUG)) && err instanceof Error) {
      console.error(err.stack);
    }
    process.exit(1);
  });
}
export {
  runTui
};
