#!/usr/bin/env node
var __require = /* @__PURE__ */ ((x2) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x2, {
  get: (a2, b2) => (typeof require !== "undefined" ? require : a2)[b2]
}) : x2)(function(x2) {
  if (typeof require !== "undefined") return require.apply(this, arguments);
  throw Error('Dynamic require of "' + x2 + '" is not supported');
});

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

// src/cli.ts
import { Command } from "commander";

// src/utils/awaitable-log.ts
function awaitableLog(logValue) {
  return new Promise((resolve4) => {
    process.stdout.write(logValue, () => {
      resolve4();
    });
  });
}

// ../../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" };
  }
}

// src/handlers/connect-timeout.ts
var DEFAULT_CONNECT_TIMEOUT_MS = 15e3;
function withConnectTimeout(settings, connectionTimeout) {
  if (connectionTimeout === void 0) return settings;
  if (settings) return { ...settings, connectionTimeout };
  return {
    headers: [],
    metadata: [],
    env: [],
    connectionTimeout,
    requestTimeout: 0,
    taskTtl: DEFAULT_TASK_TTL_MS,
    maxFetchRequests: DEFAULT_MAX_FETCH_REQUESTS,
    autoRefreshOnListChanged: false,
    paginatedLists: false,
    roots: []
  };
}

// ../../core/auth/node/secret-store.ts
import { AsyncEntry, findCredentialsAsync } from "@napi-rs/keyring";

// ../../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/auth/node/secret-store.ts
var SERVICE_NAME = "mcp-inspector";
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) {
    super(
      `OS keychain is not available. On Linux, install libsecret / gnome-keyring. Underlying error: ${cause instanceof Error ? cause.message : String(cause)}`
    );
    this.name = "KeychainUnavailableError";
  }
};
var KeyringSecretStore = class {
  async get(serverId, field) {
    const entry = new AsyncEntry(SERVICE_NAME, buildAccount(serverId, field));
    try {
      const v2 = await entry.getPassword();
      return v2 ?? null;
    } catch {
      return null;
    }
  }
  async set(serverId, field, value) {
    const entry = new AsyncEntry(SERVICE_NAME, buildAccount(serverId, field));
    try {
      await entry.setPassword(value);
    } catch (err) {
      throw new KeychainUnavailableError(err);
    }
  }
  async delete(serverId, field) {
    const entry = new AsyncEntry(SERVICE_NAME, buildAccount(serverId, field));
    try {
      await entry.deleteCredential();
    } catch {
    }
  }
  async deleteAllForServer(serverId) {
    let creds;
    try {
      creds = await 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);
    }
  }
};
var InMemorySecretStore = class {
  map = /* @__PURE__ */ new Map();
  async get(serverId, field) {
    return this.map.get(buildAccount(serverId, field)) ?? null;
  }
  async set(serverId, field, value) {
    this.map.set(buildAccount(serverId, field), value);
  }
  async delete(serverId, field) {
    this.map.delete(buildAccount(serverId, field));
  }
  async deleteAllForServer(serverId) {
    const prefix = `${serverId}:`;
    for (const key of [...this.map.keys()]) {
      if (key.startsWith(prefix)) this.map.delete(key);
    }
  }
};

// ../../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/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((r2) => {
    if (typeof r2?.uri !== "string") {
      console.warn("Dropping root without a string `uri`:", r2);
      return false;
    }
    return r2.uri.trim() !== "";
  }).map((r2) => {
    const rawName = r2.name;
    if (rawName !== void 0 && typeof rawName !== "string") {
      console.warn("Dropping non-string `name` on root:", r2);
    }
    const trimmedName = typeof rawName === "string" ? rawName.trim() : void 0;
    const { name: _name, ...rest } = r2;
    return trimmedName ? { ...rest, name: trimmedName } : rest;
  });
}
function envRecordToPairs(env) {
  return env ? Object.entries(env).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 [k2, v2] of Object.entries(toRecord(raw))) {
      if (INSPECTOR_FIELD_KEYS.has(k2)) {
        inspectorRecord[k2] = v2;
      } else {
        sdkOnly[k2] = v2;
      }
    }
    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 k2 of Object.keys(out.env)) {
      const val = secrets[envSecretField(k2)];
      if (val !== void 0) {
        newEnv[k2] = 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 k2 of Object.keys(stored.env)) {
      fields.push(envSecretField(k2));
    }
  }
  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}`);
    }
    throw new Error("Error loading configuration: Unknown 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 t2 = options.transport === "http" ? "streamable-http" : options.transport;
    if (t2 === "sse" || t2 === "streamable-http") {
      transportType = t2;
    } else {
      const u2 = new URL(url);
      if (u2.pathname.endsWith("/mcp")) {
        transportType = "streamable-http";
      } else if (u2.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/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)
    }
  };
}
function selectServerEntry(entries, serverName) {
  const names = Object.keys(entries);
  if (serverName) {
    const entry = entries[serverName];
    if (!entry) {
      throw new Error(
        `Server '${serverName}' not found. Available servers: ${names.join(", ")}`
      );
    }
    return entry;
  }
  if (names.length === 0) {
    throw new Error("No servers found in config file");
  }
  if (names.length > 1) {
    throw new Error(
      `Multiple servers found in config file. Please specify one with --server. Available servers: ${names.join(", ")}`
    );
  }
  return entries[names[0]];
}

// ../../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})`);
  }
}
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 array2 = new Uint8Array(32);
  crypto.getRandomValues(array2);
  return Array.from(array2, (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").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 };
  }
}

// src/handlers/servers-list.ts
var REDACTED = "[redacted]";
function summarizeServerConfig(config) {
  if (config.type === "sse" || config.type === "streamable-http") {
    return { type: config.type, detail: config.url ?? "" };
  }
  const args = config.args?.length ? ` ${config.args.join(" ")}` : "";
  return { type: "stdio", detail: `${config.command}${args}` };
}
async function listServerEntries(serverOptions = {}) {
  const entries = await loadServerEntries({
    ...serverOptions,
    secretStore: serverOptions.secretStore ?? new InMemorySecretStore()
  });
  return Object.entries(entries).map(([name, resolved]) => {
    const { type, detail } = summarizeServerConfig(resolved.config);
    return { name, type, detail };
  }).sort((a2, b2) => a2.name.localeCompare(b2.name));
}
async function showServerEntry(serverName, serverOptions = {}) {
  const name = serverName.trim();
  if (!name) {
    throw new Error("servers/show requires a server name.");
  }
  const entries = await loadServerEntries(serverOptions);
  const selected = selectServerEntry(entries, name);
  const { type, detail } = summarizeServerConfig(selected.config);
  const result = {
    name,
    type,
    detail,
    config: sanitizeServerConfig(selected.config)
  };
  if (selected.settings) {
    result.settings = sanitizeServerSettings(selected.settings);
  }
  return result;
}
function sanitizeServerConfig(config) {
  const out = { ...config };
  if ("env" in config && config.env) {
    out.env = redactStringRecord(config.env);
  }
  if ("requestInit" in config && isPlainObject(config.requestInit)) {
    out.requestInit = sanitizeInitRecord(config.requestInit);
  }
  if ("eventSourceInit" in config && isPlainObject(config.eventSourceInit)) {
    out.eventSourceInit = sanitizeInitRecord(config.eventSourceInit);
  }
  return out;
}
function sanitizeServerSettings(settings) {
  const out = {
    ...settings,
    headers: (settings.headers ?? []).map((h2) => ({
      key: h2.key,
      value: isSensitiveHeader(h2.key) ? REDACTED : h2.value
    })),
    metadata: (settings.metadata ?? []).map((m) => ({
      key: m.key,
      value: isSensitiveHeader(m.key) ? REDACTED : m.value
    })),
    env: (settings.env ?? []).map((e2) => ({
      key: e2.key,
      value: REDACTED
    }))
  };
  if (settings.oauthClientSecret !== void 0) {
    out.oauthClientSecret = REDACTED;
  }
  return out;
}
function redactStringRecord(record2) {
  return Object.fromEntries(Object.keys(record2).map((key) => [key, REDACTED]));
}
function isPlainObject(value) {
  return value !== null && typeof value === "object" && !Array.isArray(value);
}
function sanitizeInitRecord(init) {
  const out = { ...init };
  if (isPlainObject(init.headers)) {
    out.headers = Object.fromEntries(
      Object.entries(init.headers).map(([key, value]) => [
        key,
        isSensitiveHeader(key) ? REDACTED : value
      ])
    );
  } else if (Array.isArray(init.headers)) {
    out.headers = init.headers.map((entry) => {
      if (Array.isArray(entry) && entry.length >= 2 && typeof entry[0] === "string") {
        const key = entry[0];
        const value = entry[1];
        return [key, isSensitiveHeader(key) ? REDACTED : value];
      }
      return entry;
    });
  }
  return out;
}
function isSensitiveHeader(key) {
  const k2 = key.toLowerCase();
  return k2.includes("auth") || k2.includes("cookie") || k2.includes("secret") || k2.includes("token") || k2.includes("password") || k2.includes("api-key") || k2.includes("apikey");
}

// src/handlers/format-output.ts
async function writeFormattedResult(result, format = "text") {
  if (format === "json") {
    await awaitableLog(JSON.stringify({ result }) + "\n");
    return;
  }
  await awaitableLog(JSON.stringify(result, null, 2) + "\n");
}

// ../../core/auth/oauth-storage.ts
import {
  OAuthClientInformationSchema,
  OAuthTokensSchema
} 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 OAuthTokensSchema.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 isRecord(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 (!isRecord(parsed)) {
    return null;
  }
  if (isRecord(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();
var storageCache = /* @__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;
}
function resetNodeOAuthStorageCache(stateFilePath) {
  const key = getStateFilePath(stateFilePath);
  memoryCache.delete(key);
  storageCache.delete(key);
}
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 })
    );
  }
};

// src/clear-stored-auth-for-relogin.ts
function normalizeServerUrl(serverUrl) {
  try {
    return new URL(serverUrl).href;
  } catch {
    return serverUrl;
  }
}
async function clearStoredAuthForRelogin(serverUrl) {
  if (!serverUrl?.trim()) return;
  const raw = serverUrl.trim();
  const normalized = normalizeServerUrl(raw);
  const storage = new NodeOAuthStorage();
  await storage.clear(raw);
  if (normalized !== raw) {
    await storage.clear(normalized);
  }
  resetNodeOAuthStorageCache();
}

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

// ../../core/json/xMcpHeader.ts
var X_MCP_HEADER_KEY = "x-mcp-header";
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(path2) {
  return path2.length === 0 ? "<root>" : path2.join(".");
}
function isRecord2(node) {
  return node !== null && typeof node === "object";
}
function scanXMcpHeaderDeclarations(inputSchema) {
  const declarations = [];
  const seenLower = /* @__PURE__ */ new Map();
  const visit = (node, path2, reachable) => {
    if (!isRecord2(node)) return void 0;
    const schema = node;
    if (X_MCP_HEADER_KEY in schema) {
      if (!reachable || path2.length === 0) {
        return `${pathName(path2)}: 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(path2)}: x-mcp-header MUST be a non-empty string`;
      }
      if (!RFC9110_TOKEN.test(raw)) {
        return `${pathName(path2)}: 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(path2)}: 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: path2, headerName: raw, type });
    }
    const properties = schema.properties;
    if (isRecord2(properties)) {
      for (const [key, child] of Object.entries(properties)) {
        const fault2 = visit(child, [...path2, key], reachable);
        if (fault2 !== void 0) return fault2;
      }
    }
    for (const k2 of NON_REACHABLE_SUBSCHEMA_KEYWORDS) {
      const sub = schema[k2];
      if (sub === void 0) continue;
      const branches = Array.isArray(sub) ? sub : isRecord2(sub) && OBJECT_VALUED_SUBSCHEMA_KEYWORDS.has(k2) ? Object.values(sub) : [sub];
      for (const branch of branches) {
        const fault2 = visit(branch, [...path2, `<${k2}>`], 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 };
}

// ../../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 } 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 = z.enum([
  "working",
  "input_required",
  "completed",
  "failed",
  "cancelled"
]);
var ModernDetailedTaskSchema = z.looseObject({
  taskId: z.string(),
  status: ModernTaskStatusSchema,
  statusMessage: z.string().optional(),
  createdAt: z.string(),
  lastUpdatedAt: z.string(),
  ttlMs: z.number().nullable().optional(),
  pollIntervalMs: z.number().optional(),
  /** Present on `completed`: the original request's result (e.g. CallToolResult). */
  result: z.record(z.string(), z.unknown()).optional(),
  /** Present on `failed`: the JSON-RPC error that ended the task. */
  error: z.record(z.string(), z.unknown()).optional(),
  /** Present on `input_required`: embedded server→client requests, keyed by id. */
  inputRequests: z.record(z.string(), z.unknown()).optional()
});
var ModernGetTaskResultSchema = ModernDetailedTaskSchema;
var ModernUpdateTaskResultSchema = z.looseObject({});
var ModernCancelTaskResultSchema = z.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 z2 } from "zod/v4";
var TasksListChangedNotificationSchema = NotificationSchema.extend({
  method: z2.literal("notifications/tasks/list_changed"),
  params: z2.record(z2.string(), z2.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;
  }
};

// ../../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 record2 = body;
    const parts = [step];
    if (record2.error) parts.push(`error=${record2.error}`);
    if (record2.error_description) parts.push(record2.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})`);
  }
}

// ../../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 as OAuthTokensSchema2 } 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 OAuthTokensSchema2.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 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 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;
  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;
  }
  /**
   * 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 h2 = {
      "Content-Type": "application/json"
    };
    if (this.options.authToken) {
      h2["x-mcp-remote-auth"] = `Bearer ${this.options.authToken}`;
    }
    return h2;
  }
  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
      }
    };
    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 literal2 = schema.shape?.method?.value;
    if (typeof literal2 === "string") return literal2;
  }
  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;
  /** 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` when no URI is subscribed or on the legacy era.
  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) => {
        const entry = {
          id: crypto.randomUUID(),
          timestamp: /* @__PURE__ */ new Date(),
          direction: "request",
          origin,
          message
        };
        this.dispatchTypedEvent("message", entry);
      },
      trackResponse: (message, origin) => {
        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 record2 = {
      task,
      payloadPromise,
      resolvePayload,
      rejectPayload
    };
    record2.cleanupTimeoutId = setTimeout(() => {
      record2.cleanupTimeoutId = void 0;
      this.receiverTaskRecords.delete(taskId);
    }, ttlMs);
    this.receiverTaskRecords.set(taskId, record2);
    return record2;
  }
  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 record2 = this.receiverTaskRecords.get(updatedTask.taskId);
    if (record2) {
      record2.task = updatedTask;
      this.emitReceiverTaskStatus(updatedTask);
    }
  }
  getReceiverTask(taskId) {
    return this.receiverTaskRecords.get(taskId);
  }
  listReceiverTasks() {
    return Array.from(this.receiverTaskRecords.values()).map((r2) => r2.task);
  }
  async getReceiverTaskPayload(taskId) {
    const record2 = this.receiverTaskRecords.get(taskId);
    if (!record2) {
      throw new ProtocolError2(
        ProtocolErrorCode2.InvalidParams,
        `Unknown taskId: ${taskId}`
      );
    }
    return record2.payloadPromise;
  }
  cancelReceiverTask(taskId) {
    const record2 = this.receiverTaskRecords.get(taskId);
    if (!record2) {
      throw new ProtocolError2(
        ProtocolErrorCode2.InvalidParams,
        `Unknown taskId: ${taskId}`
      );
    }
    if (_InspectorClient.isTerminalTaskStatus(record2.task.status)) {
      return record2.task;
    }
    const now = (/* @__PURE__ */ new Date()).toISOString();
    const updatedTask = {
      ...record2.task,
      status: "cancelled",
      lastUpdatedAt: now
    };
    record2.task = updatedTask;
    record2.rejectPayload(new Error("Task cancelled"));
    if (record2.cleanupTimeoutId != null) {
      clearTimeout(record2.cleanupTimeoutId);
      record2.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 record2 = this.createReceiverTask({
            ttl: paramsTask.ttl,
            initialStatus: "input_required",
            statusMessage: "Awaiting user input"
          });
          void (async () => {
            const samplingRequest = new SamplingCreateMessage(
              request,
              (result) => {
                record2.resolvePayload(result);
                const now = (/* @__PURE__ */ new Date()).toISOString();
                const updated = {
                  ...record2.task,
                  status: "completed",
                  lastUpdatedAt: now
                };
                record2.task = updated;
                this.upsertReceiverTask(updated);
              },
              (error) => {
                record2.rejectPayload(error);
                const now = (/* @__PURE__ */ new Date()).toISOString();
                const updated = {
                  ...record2.task,
                  status: "failed",
                  lastUpdatedAt: now,
                  statusMessage: error instanceof Error ? error.message : String(error)
                };
                record2.task = updated;
                this.upsertReceiverTask(updated);
              },
              (id) => this.removePendingSample(id)
            );
            this.addPendingSample(samplingRequest);
          })();
          const taskResult = { task: record2.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 record2 = this.createReceiverTask({
            ttl: paramsTask.ttl,
            initialStatus: "input_required",
            statusMessage: "Awaiting user input"
          });
          void (async () => {
            const elicitationRequest = new ElicitationCreateMessage(
              request,
              (result) => {
                record2.resolvePayload(result);
                const now = (/* @__PURE__ */ new Date()).toISOString();
                const updated = {
                  ...record2.task,
                  status: "completed",
                  lastUpdatedAt: now
                };
                record2.task = updated;
                this.upsertReceiverTask(updated);
              },
              (id) => this.removePendingElicitation(id),
              (error) => {
                record2.rejectPayload(error);
                const now = (/* @__PURE__ */ new Date()).toISOString();
                const updated = {
                  ...record2.task,
                  status: "failed",
                  lastUpdatedAt: now,
                  statusMessage: error.message
                };
                record2.task = updated;
                this.upsertReceiverTask(updated);
              }
            );
            this.addPendingElicitation(elicitationRequest);
          })();
          const taskResult = { task: record2.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 record2 = this.getReceiverTask(params.taskId);
          if (!record2) {
            throw new ProtocolError2(
              ProtocolErrorCode2.InvalidParams,
              `Unknown taskId: ${params.taskId}`
            );
          }
          return record2.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 record2 of this.receiverTaskRecords.values()) {
      if (record2.cleanupTimeoutId != null) {
        clearTimeout(record2.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();
    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();
      this.dispatchTypedEvent("connect");
      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(
                (e2) => e2.request.params?.mode === "url" && e2.request.params?.elicitationId === elicitationId
              );
              if (pending) {
                pending.completeIfPending();
              }
            }
          );
        }
      }
    } 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((r2) => setTimeout(r2, 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((s2) => s2.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((e2) => e2.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(() => {
    });
    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";
  }
  /** 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(
            (e2) => presentedUrls.has(e2.url)
          );
          if (repeated) {
            const loopError = new UrlElicitationLoopError(repeated.url);
            this.dispatchFailedToolCall(
              tool,
              args,
              generalMetadata,
              toolSpecificMetadata,
              loopError.message
            );
            throw loopError;
          }
          urlElicitationAttempt++;
          for (const e2 of urlElicitations) {
            presentedUrls.add(e2.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;
      }
    }
  }
  /**
   * 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");
    }
    let convertedArgs = args;
    const stringArgs = {};
    for (const [key, value] of Object.entries(args)) {
      if (typeof value === "string") {
        stringArgs[key] = value;
      }
    }
    if (Object.keys(stringArgs).length > 0) {
      const convertedStringArgs = convertToolParameters(tool, stringArgs);
      convertedArgs = { ...args, ...convertedStringArgs };
    }
    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
    );
    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 = (t2) => {
      this.dispatchTypedEvent("toolCallTaskUpdated", {
        taskId: t2.taskId,
        task: t2
      });
      this.dispatchTypedEvent("requestorTaskUpdated", {
        taskId: t2.taskId,
        task: t2
      });
    };
    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((k2) => !keysBeforeRequest.has(k2)) : 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 {
      let convertedArgs = args;
      const stringArgs = {};
      for (const [key, value] of Object.entries(args)) {
        if (typeof value === "string") {
          stringArgs[key] = value;
        }
      }
      if (Object.keys(stringArgs).length > 0) {
        const convertedStringArgs = convertToolParameters(tool, stringArgs);
        convertedArgs = { ...args, ...convertedStringArgs };
      }
      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);
      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)}`
          );
        }
      }
      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)}`
      );
    }
    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)}`
      );
    }
  }
  /**
   * 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 = {
      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;
  }
  /** 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
   * `subscribedResources` set (#1630). Because the stream is not resumable,
   * every filter change re-lists: the existing stream is closed and a fresh
   * `listen()` opened. With no subscribed URIs the stream is left closed.
   *
   * `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.subscribedResources.size === 0) {
      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: true,
      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.subscribedResources.size > 0;
    if (!shouldReconnect) {
      this.setModernStreamState({
        active: this.subscribedResources.size > 0,
        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 empty case is the ordinary one: nothing subscribed, no stream, inactive.
   * The non-empty one exists because a failed re-listen leaves
   * `modernSubscription` null with URIs still subscribed, 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, URIs 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 set; 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.subscribedResources.size === 0) {
      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: true,
      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.subscribedResources.size === 0) {
        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.subscribedResources.size === 0) {
      this.setModernStreamState({
        active: this.subscribedResources.size > 0,
        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)}`
      );
    }
  }
  /**
   * 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)}`
      );
    }
  }
  // ============================================================================
  // 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/node/hostUrl.ts
import { isIPv6 } from "net";
function stripBrackets(host) {
  return host.replace(/^\[(.*)\]$/, "$1");
}
function formatHostForUrl(host) {
  const h2 = host.trim();
  const bare = stripBrackets(h2).split("%")[0];
  return isIPv6(bare) ? `[${bare}]` : h2;
}
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;
  }
}
var ALL_INTERFACES_LITERALS = /* @__PURE__ */ new Set(["", "0.0.0.0", "::", "::ffff:0:0"]);
function canonicalizeIpv6(value) {
  if (!isIPv6(value)) return value;
  const [address] = value.split("%");
  return new URL(`http://[${address}]`).hostname.slice(1, -1);
}
function parseAddressPart(part) {
  if (/^0x[0-9a-f]+$/.test(part)) return parseInt(part, 16);
  if (/^[0-9]+$/.test(part)) return parseInt(part, 10);
  return NaN;
}
function isAllZeroIpv4(value) {
  const parts = value.split(".");
  if (parts.length > 4) return false;
  return parts.every((part) => parseAddressPart(part) === 0);
}
function isAllInterfacesHost(host) {
  const normalized = canonicalizeIpv6(stripBrackets(canonicalUrlHost(host)));
  return ALL_INTERFACES_LITERALS.has(normalized) || isAllZeroIpv4(normalized);
}
function isLoopbackHost(host) {
  const h2 = canonicalUrlHost(host).replace(/\.$/, "");
  return h2 === "localhost" || h2 === "[::1]" || /^127(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/.test(h2);
}

// src/error-handler.ts
var EXIT_CODES = {
  OK: 0,
  USAGE: 1,
  NO_APP: 2,
  AUTH_REQUIRED: 3,
  UNREACHABLE: 4,
  TOOL_ERROR: 5
};
var CliExitCodeError = class extends Error {
  constructor(exitCode, message, envelope) {
    super(message);
    this.exitCode = exitCode;
    this.envelope = envelope;
    this.name = "CliExitCodeError";
  }
  exitCode;
  envelope;
};
var MAX_CAUSE_DEPTH = 16;
function causeOf(error, depth = 0) {
  if (depth >= MAX_CAUSE_DEPTH) return void 0;
  if (!(error instanceof Error)) return void 0;
  const cause = error.cause;
  if (cause === void 0 || cause === null) return void 0;
  if (cause instanceof Error) {
    const nested = causeOf(cause, depth + 1);
    return nested ? `${cause.message}: ${nested}` : cause.message;
  }
  return String(cause);
}
function statusOf(error) {
  if (error == null || typeof error !== "object") return void 0;
  const e2 = error;
  if (typeof e2.status === "number") return e2.status;
  if (typeof e2.response?.status === "number") return e2.response.status;
  if (typeof e2.code === "number" && e2.code >= 100 && e2.code <= 599) {
    return e2.code;
  }
  return void 0;
}
var UNREACHABLE_PATTERN = /ENOTFOUND|ECONNREFUSED|ECONNRESET|EAI_AGAIN|ETIMEDOUT|fetch failed|getaddrinfo|connect timed out|aborted/i;
function classifyError(error, context) {
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
  const cause = causeOf(error);
  const status = statusOf(error);
  const url = context?.url;
  if (error instanceof CliExitCodeError) {
    return {
      exitCode: error.exitCode,
      envelope: {
        code: error.envelope?.code ?? codeForExit(error.exitCode),
        message,
        ...cause !== void 0 && { cause },
        ...error.envelope?.status !== void 0 && {
          status: error.envelope.status
        },
        ...(error.envelope?.url ?? url) !== void 0 && {
          url: error.envelope?.url ?? url
        }
      }
    };
  }
  if (status === 401 || status === 403 || /WWW-Authenticate|Unauthorized|invalid_token|OAuth/i.test(
    message + " " + (cause ?? "")
  )) {
    return {
      exitCode: EXIT_CODES.AUTH_REQUIRED,
      envelope: {
        code: "auth_required",
        message,
        ...cause !== void 0 && { cause },
        ...status !== void 0 && { status },
        ...url !== void 0 && { url }
      }
    };
  }
  if (UNREACHABLE_PATTERN.test(message + " " + (cause ?? ""))) {
    return {
      exitCode: EXIT_CODES.UNREACHABLE,
      envelope: {
        code: "unreachable",
        message,
        ...cause !== void 0 && { cause },
        ...status !== void 0 && { status },
        ...url !== void 0 && { url }
      }
    };
  }
  return {
    exitCode: EXIT_CODES.USAGE,
    envelope: {
      code: "error",
      message,
      ...cause !== void 0 && { cause },
      ...status !== void 0 && { status },
      ...url !== void 0 && { url }
    }
  };
}
function codeForExit(exitCode) {
  switch (exitCode) {
    case EXIT_CODES.NO_APP:
      return "no_app";
    case EXIT_CODES.AUTH_REQUIRED:
      return "auth_required";
    case EXIT_CODES.UNREACHABLE:
      return "unreachable";
    case EXIT_CODES.TOOL_ERROR:
      return "tool_error";
    default:
      return "error";
  }
}
function formatErrorOutput(error, context) {
  const { exitCode, envelope } = classifyError(error, context);
  return {
    exitCode,
    stderr: JSON.stringify({ error: envelope }) + "\n"
  };
}
function handleError(error) {
  const { exitCode, stderr } = formatErrorOutput(error);
  process.stderr.write(stderr);
  process.exit(exitCode);
}

// src/handlers/emit-result.ts
async function emitResult(result, appInfo, args) {
  const json = args.format === "json";
  if (args.appInfo) {
    const info = appInfo ?? {
      hasApp: false,
      toolName: args.toolName ?? ""
    };
    await awaitableLog(JSON.stringify(json ? { appInfo: info } : info) + "\n");
    if (!info.hasApp) {
      throw new CliExitCodeError(
        EXIT_CODES.NO_APP,
        `Tool '${args.toolName}' has no MCP App UI resource (_meta.ui.resourceUri).`
      );
    }
    return;
  }
  if (json) {
    const envelope = { result };
    if (appInfo?.hasApp) envelope.appInfo = appInfo;
    await awaitableLog(JSON.stringify(envelope) + "\n");
  } else {
    await awaitableLog(JSON.stringify(result, null, 2) + "\n");
  }
  if (result.isError === true) {
    throw new CliExitCodeError(
      EXIT_CODES.TOOL_ERROR,
      `Tool '${args.toolName}' returned isError:true.`,
      { code: "tool_is_error" }
    );
  }
}

// src/handlers/consume-outcome.ts
async function consumeMethodOutcome(outcome, args) {
  if (outcome.kind === "result") {
    await emitResult(outcome.result, outcome.appInfo, args);
    return;
  }
  if (outcome.kind === "ndjson") {
    for (const line of outcome.lines) {
      await awaitableLog(JSON.stringify(line) + "\n");
    }
    return;
  }
  await new Promise((resolve4) => {
    const stop = outcome.start((obj) => {
      void awaitableLog(JSON.stringify(obj) + "\n");
    });
    const onSignal = () => {
      stop();
      process.off("SIGINT", onSignal);
      process.off("SIGTERM", onSignal);
      resolve4();
    };
    process.on("SIGINT", onSignal);
    process.on("SIGTERM", onSignal);
  });
}

// ../../core/mcp/state/managedListState.ts
var DEFAULT_LIST_CHANGED_DEBOUNCE_MS = 250;
var ManagedListState = class extends TypedEventTarget {
  items = [];
  client = null;
  unsubscribe = null;
  _metadata = void 0;
  listChanged = false;
  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();
    };
    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.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");
        } 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);
  }
  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.
   */
  async refresh(metadata, cacheMode) {
    const next = await this.fetchItems(metadata, cacheMode);
    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, {
      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 onStatusChange = () => {
      if (isTerminalStatus(this.client?.getStatus())) {
        this.messages = [];
        this.pendingRequestEntries.clear();
        this.dispatchTypedEvent("messagesChange", []);
      }
    };
    this.client.addEventListener("message", onMessage);
    this.client.addEventListener("statusChange", onStatusChange);
    this.unsubscribe = () => {
      if (this.client) {
        this.client.removeEventListener("message", onMessage);
        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/managedResourcesState.ts
var ManagedResourcesState = class extends ManagedListState {
  constructor(client, debounceMs = DEFAULT_LIST_CHANGED_DEBOUNCE_MS) {
    super(client, {
      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, {
      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, {
      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/mcp/state/mergeTaskIntoList.ts
function mergeTaskIntoList(tasks, taskId, task) {
  const normalized = {
    ...task,
    taskId,
    createdAt: task.createdAt ?? task.lastUpdatedAt ?? ""
  };
  const idx = tasks.findIndex((t2) => t2.taskId === taskId);
  if (idx < 0) {
    return [normalized, ...tasks];
  }
  const next = [...tasks];
  next[idx] = normalized;
  return next;
}

// ../../core/mcp/state/managedRequestorTasksState.ts
var MAX_PAGES = 100;
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
  "completed",
  "failed",
  "cancelled"
]);
var ManagedRequestorTasksState = class extends TypedEventTarget {
  tasks = [];
  client = null;
  unsubscribe = null;
  // Task ids the user dismissed via clearCompleted(). Sticky for the session:
  // these are filtered out of refresh() results and ignored by the live-merge
  // handlers so a late status update (or a server that still lists the task)
  // can't resurrect a cleared task. Reset on disconnect / destroy.
  dismissedTaskIds = /* @__PURE__ */ new Set();
  // Task ids the user cancelled via the Cancel Task control. Cancellation is a
  // deliberate terminal decision, so it is sticky: a status update that arrives
  // afterwards — e.g. a server that completes the task anyway (cancellation is
  // cooperative) or the in-flight tool call resolving with a result — must not
  // flip a cancelled task back to "completed". Reset on disconnect / destroy.
  cancelledTaskIds = /* @__PURE__ */ new Set();
  constructor(client) {
    super();
    this.client = client;
    const onConnect = () => {
      void this.refresh();
    };
    const onTasksListChanged = () => {
      void this.refresh();
    };
    const onStatusChange = () => {
      if (isTerminalStatus(this.client?.getStatus())) {
        this.tasks = [];
        this.dismissedTaskIds.clear();
        this.cancelledTaskIds.clear();
        this.dispatchTypedEvent("tasksChange", []);
      }
    };
    const shouldIgnoreUpdate = (taskId, status) => this.dismissedTaskIds.has(taskId) || this.cancelledTaskIds.has(taskId) && status !== "cancelled";
    const onTaskStatusChange = (e2) => {
      const { taskId, task } = e2.detail;
      if (shouldIgnoreUpdate(taskId, task.status)) return;
      this.tasks = mergeTaskIntoList(this.tasks, taskId, task);
      this.dispatchTypedEvent("tasksChange", this.tasks);
    };
    const onRequestorTaskUpdated = (e2) => {
      const { taskId, task } = e2.detail;
      if (shouldIgnoreUpdate(taskId, task.status)) return;
      this.tasks = mergeTaskIntoList(this.tasks, taskId, task);
      this.dispatchTypedEvent("tasksChange", this.tasks);
    };
    const onTaskCancelled = (e2) => {
      const { taskId } = e2.detail;
      if (this.dismissedTaskIds.has(taskId)) return;
      this.cancelledTaskIds.add(taskId);
      const idx = this.tasks.findIndex((t2) => t2.taskId === taskId);
      if (idx >= 0) {
        const next = [...this.tasks];
        const prev = next[idx];
        next[idx] = { ...prev, status: "cancelled" };
        this.tasks = next;
        this.dispatchTypedEvent("tasksChange", this.tasks);
      }
    };
    this.client.addEventListener("connect", onConnect);
    this.client.addEventListener("tasksListChanged", onTasksListChanged);
    this.client.addEventListener("statusChange", onStatusChange);
    this.client.addEventListener("taskStatusChange", onTaskStatusChange);
    this.client.addEventListener(
      "requestorTaskUpdated",
      onRequestorTaskUpdated
    );
    this.client.addEventListener("taskCancelled", onTaskCancelled);
    this.unsubscribe = () => {
      if (this.client) {
        this.client.removeEventListener("connect", onConnect);
        this.client.removeEventListener("tasksListChanged", onTasksListChanged);
        this.client.removeEventListener("statusChange", onStatusChange);
        this.client.removeEventListener("taskStatusChange", onTaskStatusChange);
        this.client.removeEventListener(
          "requestorTaskUpdated",
          onRequestorTaskUpdated
        );
        this.client.removeEventListener("taskCancelled", onTaskCancelled);
      }
      this.client = null;
    };
  }
  getTasks() {
    return [...this.tasks];
  }
  async refresh() {
    const client = this.client;
    if (!client || client.getStatus() !== "connected") {
      return this.getTasks();
    }
    if (client.isTasksExtensionNegotiated()) {
      return this.refreshModern(client);
    }
    if (!client.getCapabilities()?.tasks) {
      this.tasks = [];
      this.dispatchTypedEvent("tasksChange", this.tasks);
      return this.getTasks();
    }
    this.tasks = [];
    let cursor;
    let pageCount = 0;
    do {
      const result = await client.listRequestorTasks(cursor);
      const page = result.tasks.filter((t2) => !this.dismissedTaskIds.has(t2.taskId)).map(
        (t2) => this.cancelledTaskIds.has(t2.taskId) ? { ...t2, status: "cancelled" } : t2
      );
      this.tasks = cursor ? [...this.tasks, ...page] : page;
      cursor = result.nextCursor;
      pageCount++;
      if (pageCount >= MAX_PAGES) {
        throw new Error(
          `Maximum pagination limit (${MAX_PAGES} pages) reached while listing requestor tasks`
        );
      }
    } while (cursor);
    this.dispatchTypedEvent("tasksChange", this.tasks);
    return this.getTasks();
  }
  /**
   * Modern refresh: re-poll each currently-known (non-dismissed) task via
   * `getRequestorTask` (`tasks/get`). Each poll dispatches `requestorTaskUpdated`,
   * which the live-merge handler folds back into `this.tasks` — so the list
   * updates in place. A poll that errors (e.g. the task expired and the server
   * returns "unknown taskId") is swallowed so one dead handle can't abort the
   * whole refresh. No known tasks ⇒ a no-op refresh (the correct "no server
   * list" semantics).
   */
  async refreshModern(client) {
    const ids = this.tasks.map((t2) => t2.taskId).filter((id) => !this.dismissedTaskIds.has(id));
    for (const id of ids) {
      try {
        await client.getRequestorTask(id);
      } catch {
      }
    }
    return this.getTasks();
  }
  /**
   * Drop terminal-state tasks (completed / failed / cancelled) from the list.
   * Their ids are remembered in `dismissedTaskIds` so they stay gone for the
   * rest of the session — a subsequent refresh() or live update won't bring
   * them back. No-op (and no event) when there's nothing terminal to clear.
   */
  clearCompleted() {
    const remaining = [];
    let dismissedAny = false;
    for (const task of this.tasks) {
      if (TERMINAL_STATUSES.has(task.status)) {
        this.dismissedTaskIds.add(task.taskId);
        dismissedAny = true;
      } else {
        remaining.push(task);
      }
    }
    if (!dismissedAny) return;
    this.tasks = remaining;
    this.dispatchTypedEvent("tasksChange", this.tasks);
  }
  destroy() {
    this.unsubscribe?.();
    this.unsubscribe = null;
    this.tasks = [];
    this.dismissedTaskIds.clear();
    this.cancelledTaskIds.clear();
  }
};

// ../../node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
import * as z3 from "zod/v4";
var RELATED_TASK_META_KEY4 = "io.modelcontextprotocol/related-task";
var JSONRPC_VERSION = "2.0";
var AssertObjectSchema = z3.custom((v2) => v2 !== null && (typeof v2 === "object" || typeof v2 === "function"));
var ProgressTokenSchema = z3.union([z3.string(), z3.number().int()]);
var CursorSchema = z3.string();
var TaskCreationParamsSchema = z3.looseObject({
  /**
   * Requested duration in milliseconds to retain task from creation.
   */
  ttl: z3.number().optional(),
  /**
   * Time in milliseconds to wait between task status requests.
   */
  pollInterval: z3.number().optional()
});
var TaskMetadataSchema = z3.object({
  ttl: z3.number().optional()
});
var RelatedTaskMetadataSchema = z3.object({
  taskId: z3.string()
});
var RequestMetaSchema = z3.looseObject({
  /**
   * If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.
   */
  progressToken: ProgressTokenSchema.optional(),
  /**
   * If specified, this request is related to the provided task.
   */
  [RELATED_TASK_META_KEY4]: RelatedTaskMetadataSchema.optional()
});
var BaseRequestParamsSchema = z3.object({
  /**
   * See [General fields: `_meta`](/specification/draft/basic/index#meta) for notes on `_meta` usage.
   */
  _meta: RequestMetaSchema.optional()
});
var TaskAugmentedRequestParamsSchema = BaseRequestParamsSchema.extend({
  /**
   * If specified, the caller is requesting task-augmented execution for this request.
   * The request will return a CreateTaskResult immediately, and the actual result can be
   * retrieved later via tasks/result.
   *
   * Task augmentation is subject to capability negotiation - receivers MUST declare support
   * for task augmentation of specific request types in their capabilities.
   */
  task: TaskMetadataSchema.optional()
});
var RequestSchema = z3.object({
  method: z3.string(),
  params: BaseRequestParamsSchema.loose().optional()
});
var NotificationsParamsSchema = z3.object({
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: RequestMetaSchema.optional()
});
var NotificationSchema2 = z3.object({
  method: z3.string(),
  params: NotificationsParamsSchema.loose().optional()
});
var ResultSchema = z3.looseObject({
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: RequestMetaSchema.optional()
});
var RequestIdSchema = z3.union([z3.string(), z3.number().int()]);
var JSONRPCRequestSchema = z3.object({
  jsonrpc: z3.literal(JSONRPC_VERSION),
  id: RequestIdSchema,
  ...RequestSchema.shape
}).strict();
var JSONRPCNotificationSchema = z3.object({
  jsonrpc: z3.literal(JSONRPC_VERSION),
  ...NotificationSchema2.shape
}).strict();
var JSONRPCResultResponseSchema = z3.object({
  jsonrpc: z3.literal(JSONRPC_VERSION),
  id: RequestIdSchema,
  result: ResultSchema
}).strict();
var ErrorCode;
(function(ErrorCode2) {
  ErrorCode2[ErrorCode2["ConnectionClosed"] = -32e3] = "ConnectionClosed";
  ErrorCode2[ErrorCode2["RequestTimeout"] = -32001] = "RequestTimeout";
  ErrorCode2[ErrorCode2["ParseError"] = -32700] = "ParseError";
  ErrorCode2[ErrorCode2["InvalidRequest"] = -32600] = "InvalidRequest";
  ErrorCode2[ErrorCode2["MethodNotFound"] = -32601] = "MethodNotFound";
  ErrorCode2[ErrorCode2["InvalidParams"] = -32602] = "InvalidParams";
  ErrorCode2[ErrorCode2["InternalError"] = -32603] = "InternalError";
  ErrorCode2[ErrorCode2["UrlElicitationRequired"] = -32042] = "UrlElicitationRequired";
})(ErrorCode || (ErrorCode = {}));
var JSONRPCErrorResponseSchema = z3.object({
  jsonrpc: z3.literal(JSONRPC_VERSION),
  id: RequestIdSchema.optional(),
  error: z3.object({
    /**
     * The error type that occurred.
     */
    code: z3.number().int(),
    /**
     * A short description of the error. The message SHOULD be limited to a concise single sentence.
     */
    message: z3.string(),
    /**
     * Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.).
     */
    data: z3.unknown().optional()
  })
}).strict();
var JSONRPCMessageSchema = z3.union([
  JSONRPCRequestSchema,
  JSONRPCNotificationSchema,
  JSONRPCResultResponseSchema,
  JSONRPCErrorResponseSchema
]);
var JSONRPCResponseSchema = z3.union([JSONRPCResultResponseSchema, JSONRPCErrorResponseSchema]);
var EmptyResultSchema2 = ResultSchema.strict();
var CancelledNotificationParamsSchema = NotificationsParamsSchema.extend({
  /**
   * The ID of the request to cancel.
   *
   * This MUST correspond to the ID of a request previously issued in the same direction.
   */
  requestId: RequestIdSchema.optional(),
  /**
   * An optional string describing the reason for the cancellation. This MAY be logged or presented to the user.
   */
  reason: z3.string().optional()
});
var CancelledNotificationSchema = NotificationSchema2.extend({
  method: z3.literal("notifications/cancelled"),
  params: CancelledNotificationParamsSchema
});
var IconSchema = z3.object({
  /**
   * URL or data URI for the icon.
   */
  src: z3.string(),
  /**
   * Optional MIME type for the icon.
   */
  mimeType: z3.string().optional(),
  /**
   * Optional array of strings that specify sizes at which the icon can be used.
   * Each string should be in WxH format (e.g., `"48x48"`, `"96x96"`) or `"any"` for scalable formats like SVG.
   *
   * If not provided, the client should assume that the icon can be used at any size.
   */
  sizes: z3.array(z3.string()).optional(),
  /**
   * Optional specifier for the theme this icon is designed for. `light` indicates
   * the icon is designed to be used with a light background, and `dark` indicates
   * the icon is designed to be used with a dark background.
   *
   * If not provided, the client should assume the icon can be used with any theme.
   */
  theme: z3.enum(["light", "dark"]).optional()
});
var IconsSchema = z3.object({
  /**
   * Optional set of sized icons that the client can display in a user interface.
   *
   * Clients that support rendering icons MUST support at least the following MIME types:
   * - `image/png` - PNG images (safe, universal compatibility)
   * - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility)
   *
   * Clients that support rendering icons SHOULD also support:
   * - `image/svg+xml` - SVG images (scalable but requires security precautions)
   * - `image/webp` - WebP images (modern, efficient format)
   */
  icons: z3.array(IconSchema).optional()
});
var BaseMetadataSchema = z3.object({
  /** Intended for programmatic or logical use, but used as a display name in past specs or fallback */
  name: z3.string(),
  /**
   * Intended for UI and end-user contexts — optimized to be human-readable and easily understood,
   * even by those unfamiliar with domain-specific terminology.
   *
   * If not provided, the name should be used for display (except for Tool,
   * where `annotations.title` should be given precedence over using `name`,
   * if present).
   */
  title: z3.string().optional()
});
var ImplementationSchema = BaseMetadataSchema.extend({
  ...BaseMetadataSchema.shape,
  ...IconsSchema.shape,
  version: z3.string(),
  /**
   * An optional URL of the website for this implementation.
   */
  websiteUrl: z3.string().optional(),
  /**
   * An optional human-readable description of what this implementation does.
   *
   * This can be used by clients or servers to provide context about their purpose
   * and capabilities. For example, a server might describe the types of resources
   * or tools it provides, while a client might describe its intended use case.
   */
  description: z3.string().optional()
});
var FormElicitationCapabilitySchema = z3.intersection(z3.object({
  applyDefaults: z3.boolean().optional()
}), z3.record(z3.string(), z3.unknown()));
var ElicitationCapabilitySchema = z3.preprocess((value) => {
  if (value && typeof value === "object" && !Array.isArray(value)) {
    if (Object.keys(value).length === 0) {
      return { form: {} };
    }
  }
  return value;
}, z3.intersection(z3.object({
  form: FormElicitationCapabilitySchema.optional(),
  url: AssertObjectSchema.optional()
}), z3.record(z3.string(), z3.unknown()).optional()));
var ClientTasksCapabilitySchema = z3.looseObject({
  /**
   * Present if the client supports listing tasks.
   */
  list: AssertObjectSchema.optional(),
  /**
   * Present if the client supports cancelling tasks.
   */
  cancel: AssertObjectSchema.optional(),
  /**
   * Capabilities for task creation on specific request types.
   */
  requests: z3.looseObject({
    /**
     * Task support for sampling requests.
     */
    sampling: z3.looseObject({
      createMessage: AssertObjectSchema.optional()
    }).optional(),
    /**
     * Task support for elicitation requests.
     */
    elicitation: z3.looseObject({
      create: AssertObjectSchema.optional()
    }).optional()
  }).optional()
});
var ServerTasksCapabilitySchema = z3.looseObject({
  /**
   * Present if the server supports listing tasks.
   */
  list: AssertObjectSchema.optional(),
  /**
   * Present if the server supports cancelling tasks.
   */
  cancel: AssertObjectSchema.optional(),
  /**
   * Capabilities for task creation on specific request types.
   */
  requests: z3.looseObject({
    /**
     * Task support for tool requests.
     */
    tools: z3.looseObject({
      call: AssertObjectSchema.optional()
    }).optional()
  }).optional()
});
var ClientCapabilitiesSchema = z3.object({
  /**
   * Experimental, non-standard capabilities that the client supports.
   */
  experimental: z3.record(z3.string(), AssertObjectSchema).optional(),
  /**
   * Present if the client supports sampling from an LLM.
   */
  sampling: z3.object({
    /**
     * Present if the client supports context inclusion via includeContext parameter.
     * If not declared, servers SHOULD only use `includeContext: "none"` (or omit it).
     */
    context: AssertObjectSchema.optional(),
    /**
     * Present if the client supports tool use via tools and toolChoice parameters.
     */
    tools: AssertObjectSchema.optional()
  }).optional(),
  /**
   * Present if the client supports eliciting user input.
   */
  elicitation: ElicitationCapabilitySchema.optional(),
  /**
   * Present if the client supports listing roots.
   */
  roots: z3.object({
    /**
     * Whether the client supports issuing notifications for changes to the roots list.
     */
    listChanged: z3.boolean().optional()
  }).optional(),
  /**
   * Present if the client supports task creation.
   */
  tasks: ClientTasksCapabilitySchema.optional(),
  /**
   * Extensions that the client supports. Keys are extension identifiers (vendor-prefix/extension-name).
   */
  extensions: z3.record(z3.string(), AssertObjectSchema).optional()
});
var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
  /**
   * The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well.
   */
  protocolVersion: z3.string(),
  capabilities: ClientCapabilitiesSchema,
  clientInfo: ImplementationSchema
});
var InitializeRequestSchema = RequestSchema.extend({
  method: z3.literal("initialize"),
  params: InitializeRequestParamsSchema
});
var ServerCapabilitiesSchema = z3.object({
  /**
   * Experimental, non-standard capabilities that the server supports.
   */
  experimental: z3.record(z3.string(), AssertObjectSchema).optional(),
  /**
   * Present if the server supports sending log messages to the client.
   */
  logging: AssertObjectSchema.optional(),
  /**
   * Present if the server supports sending completions to the client.
   */
  completions: AssertObjectSchema.optional(),
  /**
   * Present if the server offers any prompt templates.
   */
  prompts: z3.object({
    /**
     * Whether this server supports issuing notifications for changes to the prompt list.
     */
    listChanged: z3.boolean().optional()
  }).optional(),
  /**
   * Present if the server offers any resources to read.
   */
  resources: z3.object({
    /**
     * Whether this server supports clients subscribing to resource updates.
     */
    subscribe: z3.boolean().optional(),
    /**
     * Whether this server supports issuing notifications for changes to the resource list.
     */
    listChanged: z3.boolean().optional()
  }).optional(),
  /**
   * Present if the server offers any tools to call.
   */
  tools: z3.object({
    /**
     * Whether this server supports issuing notifications for changes to the tool list.
     */
    listChanged: z3.boolean().optional()
  }).optional(),
  /**
   * Present if the server supports task creation.
   */
  tasks: ServerTasksCapabilitySchema.optional(),
  /**
   * Extensions that the server supports. Keys are extension identifiers (vendor-prefix/extension-name).
   */
  extensions: z3.record(z3.string(), AssertObjectSchema).optional()
});
var InitializeResultSchema = ResultSchema.extend({
  /**
   * The version of the Model Context Protocol that the server wants to use. This may not match the version that the client requested. If the client cannot support this version, it MUST disconnect.
   */
  protocolVersion: z3.string(),
  capabilities: ServerCapabilitiesSchema,
  serverInfo: ImplementationSchema,
  /**
   * Instructions describing how to use the server and its features.
   *
   * This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt.
   */
  instructions: z3.string().optional()
});
var InitializedNotificationSchema = NotificationSchema2.extend({
  method: z3.literal("notifications/initialized"),
  params: NotificationsParamsSchema.optional()
});
var PingRequestSchema = RequestSchema.extend({
  method: z3.literal("ping"),
  params: BaseRequestParamsSchema.optional()
});
var ProgressSchema = z3.object({
  /**
   * The progress thus far. This should increase every time progress is made, even if the total is unknown.
   */
  progress: z3.number(),
  /**
   * Total number of items to process (or total progress required), if known.
   */
  total: z3.optional(z3.number()),
  /**
   * An optional message describing the current progress.
   */
  message: z3.optional(z3.string())
});
var ProgressNotificationParamsSchema = z3.object({
  ...NotificationsParamsSchema.shape,
  ...ProgressSchema.shape,
  /**
   * The progress token which was given in the initial request, used to associate this notification with the request that is proceeding.
   */
  progressToken: ProgressTokenSchema
});
var ProgressNotificationSchema = NotificationSchema2.extend({
  method: z3.literal("notifications/progress"),
  params: ProgressNotificationParamsSchema
});
var PaginatedRequestParamsSchema = BaseRequestParamsSchema.extend({
  /**
   * An opaque token representing the current pagination position.
   * If provided, the server should return results starting after this cursor.
   */
  cursor: CursorSchema.optional()
});
var PaginatedRequestSchema = RequestSchema.extend({
  params: PaginatedRequestParamsSchema.optional()
});
var PaginatedResultSchema = ResultSchema.extend({
  /**
   * An opaque token representing the pagination position after the last returned result.
   * If present, there may be more results available.
   */
  nextCursor: CursorSchema.optional()
});
var TaskStatusSchema = z3.enum(["working", "input_required", "completed", "failed", "cancelled"]);
var TaskSchema = z3.object({
  taskId: z3.string(),
  status: TaskStatusSchema,
  /**
   * Time in milliseconds to keep task results available after completion.
   * If null, the task has unlimited lifetime until manually cleaned up.
   */
  ttl: z3.union([z3.number(), z3.null()]),
  /**
   * ISO 8601 timestamp when the task was created.
   */
  createdAt: z3.string(),
  /**
   * ISO 8601 timestamp when the task was last updated.
   */
  lastUpdatedAt: z3.string(),
  pollInterval: z3.optional(z3.number()),
  /**
   * Optional diagnostic message for failed tasks or other status information.
   */
  statusMessage: z3.optional(z3.string())
});
var CreateTaskResultSchema2 = ResultSchema.extend({
  task: TaskSchema
});
var TaskStatusNotificationParamsSchema = NotificationsParamsSchema.merge(TaskSchema);
var TaskStatusNotificationSchema2 = NotificationSchema2.extend({
  method: z3.literal("notifications/tasks/status"),
  params: TaskStatusNotificationParamsSchema
});
var GetTaskRequestSchema2 = RequestSchema.extend({
  method: z3.literal("tasks/get"),
  params: BaseRequestParamsSchema.extend({
    taskId: z3.string()
  })
});
var GetTaskResultSchema2 = ResultSchema.merge(TaskSchema);
var GetTaskPayloadRequestSchema2 = RequestSchema.extend({
  method: z3.literal("tasks/result"),
  params: BaseRequestParamsSchema.extend({
    taskId: z3.string()
  })
});
var GetTaskPayloadResultSchema = ResultSchema.loose();
var ListTasksRequestSchema2 = PaginatedRequestSchema.extend({
  method: z3.literal("tasks/list")
});
var ListTasksResultSchema2 = PaginatedResultSchema.extend({
  tasks: z3.array(TaskSchema)
});
var CancelTaskRequestSchema2 = RequestSchema.extend({
  method: z3.literal("tasks/cancel"),
  params: BaseRequestParamsSchema.extend({
    taskId: z3.string()
  })
});
var CancelTaskResultSchema2 = ResultSchema.merge(TaskSchema);
var ResourceContentsSchema = z3.object({
  /**
   * The URI of this resource.
   */
  uri: z3.string(),
  /**
   * The MIME type of this resource, if known.
   */
  mimeType: z3.optional(z3.string()),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.record(z3.string(), z3.unknown()).optional()
});
var TextResourceContentsSchema = ResourceContentsSchema.extend({
  /**
   * The text of the item. This must only be set if the item can actually be represented as text (not binary data).
   */
  text: z3.string()
});
var Base64Schema = z3.string().refine((val) => {
  try {
    atob(val);
    return true;
  } catch {
    return false;
  }
}, { message: "Invalid Base64 string" });
var BlobResourceContentsSchema = ResourceContentsSchema.extend({
  /**
   * A base64-encoded string representing the binary data of the item.
   */
  blob: Base64Schema
});
var RoleSchema = z3.enum(["user", "assistant"]);
var AnnotationsSchema = z3.object({
  /**
   * Intended audience(s) for the resource.
   */
  audience: z3.array(RoleSchema).optional(),
  /**
   * Importance hint for the resource, from 0 (least) to 1 (most).
   */
  priority: z3.number().min(0).max(1).optional(),
  /**
   * ISO 8601 timestamp for the most recent modification.
   */
  lastModified: z3.iso.datetime({ offset: true }).optional()
});
var ResourceSchema = z3.object({
  ...BaseMetadataSchema.shape,
  ...IconsSchema.shape,
  /**
   * The URI of this resource.
   */
  uri: z3.string(),
  /**
   * A description of what this resource represents.
   *
   * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.
   */
  description: z3.optional(z3.string()),
  /**
   * The MIME type of this resource, if known.
   */
  mimeType: z3.optional(z3.string()),
  /**
   * The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.
   *
   * This can be used by Hosts to display file sizes and estimate context window usage.
   */
  size: z3.optional(z3.number()),
  /**
   * Optional annotations for the client.
   */
  annotations: AnnotationsSchema.optional(),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.optional(z3.looseObject({}))
});
var ResourceTemplateSchema = z3.object({
  ...BaseMetadataSchema.shape,
  ...IconsSchema.shape,
  /**
   * A URI template (according to RFC 6570) that can be used to construct resource URIs.
   */
  uriTemplate: z3.string(),
  /**
   * A description of what this template is for.
   *
   * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.
   */
  description: z3.optional(z3.string()),
  /**
   * The MIME type for all resources that match this template. This should only be included if all resources matching this template have the same type.
   */
  mimeType: z3.optional(z3.string()),
  /**
   * Optional annotations for the client.
   */
  annotations: AnnotationsSchema.optional(),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.optional(z3.looseObject({}))
});
var ListResourcesRequestSchema = PaginatedRequestSchema.extend({
  method: z3.literal("resources/list")
});
var ListResourcesResultSchema2 = PaginatedResultSchema.extend({
  resources: z3.array(ResourceSchema)
});
var ListResourceTemplatesRequestSchema = PaginatedRequestSchema.extend({
  method: z3.literal("resources/templates/list")
});
var ListResourceTemplatesResultSchema2 = PaginatedResultSchema.extend({
  resourceTemplates: z3.array(ResourceTemplateSchema)
});
var ResourceRequestParamsSchema = BaseRequestParamsSchema.extend({
  /**
   * The URI of the resource to read. The URI can use any protocol; it is up to the server how to interpret it.
   *
   * @format uri
   */
  uri: z3.string()
});
var ReadResourceRequestParamsSchema = ResourceRequestParamsSchema;
var ReadResourceRequestSchema = RequestSchema.extend({
  method: z3.literal("resources/read"),
  params: ReadResourceRequestParamsSchema
});
var ReadResourceResultSchema2 = ResultSchema.extend({
  contents: z3.array(z3.union([TextResourceContentsSchema, BlobResourceContentsSchema]))
});
var ResourceListChangedNotificationSchema = NotificationSchema2.extend({
  method: z3.literal("notifications/resources/list_changed"),
  params: NotificationsParamsSchema.optional()
});
var SubscribeRequestParamsSchema = ResourceRequestParamsSchema;
var SubscribeRequestSchema = RequestSchema.extend({
  method: z3.literal("resources/subscribe"),
  params: SubscribeRequestParamsSchema
});
var UnsubscribeRequestParamsSchema = ResourceRequestParamsSchema;
var UnsubscribeRequestSchema = RequestSchema.extend({
  method: z3.literal("resources/unsubscribe"),
  params: UnsubscribeRequestParamsSchema
});
var ResourceUpdatedNotificationParamsSchema = NotificationsParamsSchema.extend({
  /**
   * The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to.
   */
  uri: z3.string()
});
var ResourceUpdatedNotificationSchema = NotificationSchema2.extend({
  method: z3.literal("notifications/resources/updated"),
  params: ResourceUpdatedNotificationParamsSchema
});
var PromptArgumentSchema = z3.object({
  /**
   * The name of the argument.
   */
  name: z3.string(),
  /**
   * A human-readable description of the argument.
   */
  description: z3.optional(z3.string()),
  /**
   * Whether this argument must be provided.
   */
  required: z3.optional(z3.boolean())
});
var PromptSchema = z3.object({
  ...BaseMetadataSchema.shape,
  ...IconsSchema.shape,
  /**
   * An optional description of what this prompt provides
   */
  description: z3.optional(z3.string()),
  /**
   * A list of arguments to use for templating the prompt.
   */
  arguments: z3.optional(z3.array(PromptArgumentSchema)),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.optional(z3.looseObject({}))
});
var ListPromptsRequestSchema = PaginatedRequestSchema.extend({
  method: z3.literal("prompts/list")
});
var ListPromptsResultSchema2 = PaginatedResultSchema.extend({
  prompts: z3.array(PromptSchema)
});
var GetPromptRequestParamsSchema = BaseRequestParamsSchema.extend({
  /**
   * The name of the prompt or prompt template.
   */
  name: z3.string(),
  /**
   * Arguments to use for templating the prompt.
   */
  arguments: z3.record(z3.string(), z3.string()).optional()
});
var GetPromptRequestSchema = RequestSchema.extend({
  method: z3.literal("prompts/get"),
  params: GetPromptRequestParamsSchema
});
var TextContentSchema = z3.object({
  type: z3.literal("text"),
  /**
   * The text content of the message.
   */
  text: z3.string(),
  /**
   * Optional annotations for the client.
   */
  annotations: AnnotationsSchema.optional(),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.record(z3.string(), z3.unknown()).optional()
});
var ImageContentSchema = z3.object({
  type: z3.literal("image"),
  /**
   * The base64-encoded image data.
   */
  data: Base64Schema,
  /**
   * The MIME type of the image. Different providers may support different image types.
   */
  mimeType: z3.string(),
  /**
   * Optional annotations for the client.
   */
  annotations: AnnotationsSchema.optional(),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.record(z3.string(), z3.unknown()).optional()
});
var AudioContentSchema = z3.object({
  type: z3.literal("audio"),
  /**
   * The base64-encoded audio data.
   */
  data: Base64Schema,
  /**
   * The MIME type of the audio. Different providers may support different audio types.
   */
  mimeType: z3.string(),
  /**
   * Optional annotations for the client.
   */
  annotations: AnnotationsSchema.optional(),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.record(z3.string(), z3.unknown()).optional()
});
var ToolUseContentSchema = z3.object({
  type: z3.literal("tool_use"),
  /**
   * The name of the tool to invoke.
   * Must match a tool name from the request's tools array.
   */
  name: z3.string(),
  /**
   * Unique identifier for this tool call.
   * Used to correlate with ToolResultContent in subsequent messages.
   */
  id: z3.string(),
  /**
   * Arguments to pass to the tool.
   * Must conform to the tool's inputSchema.
   */
  input: z3.record(z3.string(), z3.unknown()),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.record(z3.string(), z3.unknown()).optional()
});
var EmbeddedResourceSchema = z3.object({
  type: z3.literal("resource"),
  resource: z3.union([TextResourceContentsSchema, BlobResourceContentsSchema]),
  /**
   * Optional annotations for the client.
   */
  annotations: AnnotationsSchema.optional(),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.record(z3.string(), z3.unknown()).optional()
});
var ResourceLinkSchema = ResourceSchema.extend({
  type: z3.literal("resource_link")
});
var ContentBlockSchema = z3.union([
  TextContentSchema,
  ImageContentSchema,
  AudioContentSchema,
  ResourceLinkSchema,
  EmbeddedResourceSchema
]);
var PromptMessageSchema = z3.object({
  role: RoleSchema,
  content: ContentBlockSchema
});
var GetPromptResultSchema2 = ResultSchema.extend({
  /**
   * An optional description for the prompt.
   */
  description: z3.string().optional(),
  messages: z3.array(PromptMessageSchema)
});
var PromptListChangedNotificationSchema = NotificationSchema2.extend({
  method: z3.literal("notifications/prompts/list_changed"),
  params: NotificationsParamsSchema.optional()
});
var ToolAnnotationsSchema = z3.object({
  /**
   * A human-readable title for the tool.
   */
  title: z3.string().optional(),
  /**
   * If true, the tool does not modify its environment.
   *
   * Default: false
   */
  readOnlyHint: z3.boolean().optional(),
  /**
   * If true, the tool may perform destructive updates to its environment.
   * If false, the tool performs only additive updates.
   *
   * (This property is meaningful only when `readOnlyHint == false`)
   *
   * Default: true
   */
  destructiveHint: z3.boolean().optional(),
  /**
   * If true, calling the tool repeatedly with the same arguments
   * will have no additional effect on the its environment.
   *
   * (This property is meaningful only when `readOnlyHint == false`)
   *
   * Default: false
   */
  idempotentHint: z3.boolean().optional(),
  /**
   * If true, this tool may interact with an "open world" of external
   * entities. If false, the tool's domain of interaction is closed.
   * For example, the world of a web search tool is open, whereas that
   * of a memory tool is not.
   *
   * Default: true
   */
  openWorldHint: z3.boolean().optional()
});
var ToolExecutionSchema = z3.object({
  /**
   * Indicates the tool's preference for task-augmented execution.
   * - "required": Clients MUST invoke the tool as a task
   * - "optional": Clients MAY invoke the tool as a task or normal request
   * - "forbidden": Clients MUST NOT attempt to invoke the tool as a task
   *
   * If not present, defaults to "forbidden".
   */
  taskSupport: z3.enum(["required", "optional", "forbidden"]).optional()
});
var ToolSchema = z3.object({
  ...BaseMetadataSchema.shape,
  ...IconsSchema.shape,
  /**
   * A human-readable description of the tool.
   */
  description: z3.string().optional(),
  /**
   * A JSON Schema 2020-12 object defining the expected parameters for the tool.
   * Must have type: 'object' at the root level per MCP spec.
   */
  inputSchema: z3.object({
    type: z3.literal("object"),
    properties: z3.record(z3.string(), AssertObjectSchema).optional(),
    required: z3.array(z3.string()).optional()
  }).catchall(z3.unknown()),
  /**
   * An optional JSON Schema 2020-12 object defining the structure of the tool's output
   * returned in the structuredContent field of a CallToolResult.
   * Must have type: 'object' at the root level per MCP spec.
   */
  outputSchema: z3.object({
    type: z3.literal("object"),
    properties: z3.record(z3.string(), AssertObjectSchema).optional(),
    required: z3.array(z3.string()).optional()
  }).catchall(z3.unknown()).optional(),
  /**
   * Optional additional tool information.
   */
  annotations: ToolAnnotationsSchema.optional(),
  /**
   * Execution-related properties for this tool.
   */
  execution: ToolExecutionSchema.optional(),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.record(z3.string(), z3.unknown()).optional()
});
var ListToolsRequestSchema = PaginatedRequestSchema.extend({
  method: z3.literal("tools/list")
});
var ListToolsResultSchema2 = PaginatedResultSchema.extend({
  tools: z3.array(ToolSchema)
});
var CallToolResultSchema2 = ResultSchema.extend({
  /**
   * A list of content objects that represent the result of the tool call.
   *
   * If the Tool does not define an outputSchema, this field MUST be present in the result.
   * For backwards compatibility, this field is always present, but it may be empty.
   */
  content: z3.array(ContentBlockSchema).default([]),
  /**
   * An object containing structured tool output.
   *
   * If the Tool defines an outputSchema, this field MUST be present in the result, and contain a JSON object that matches the schema.
   */
  structuredContent: z3.record(z3.string(), z3.unknown()).optional(),
  /**
   * Whether the tool call ended in an error.
   *
   * If not set, this is assumed to be false (the call was successful).
   *
   * Any errors that originate from the tool SHOULD be reported inside the result
   * object, with `isError` set to true, _not_ as an MCP protocol-level error
   * response. Otherwise, the LLM would not be able to see that an error occurred
   * and self-correct.
   *
   * However, any errors in _finding_ the tool, an error indicating that the
   * server does not support tool calls, or any other exceptional conditions,
   * should be reported as an MCP error response.
   */
  isError: z3.boolean().optional()
});
var CompatibilityCallToolResultSchema = CallToolResultSchema2.or(ResultSchema.extend({
  toolResult: z3.unknown()
}));
var CallToolRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend({
  /**
   * The name of the tool to call.
   */
  name: z3.string(),
  /**
   * Arguments to pass to the tool.
   */
  arguments: z3.record(z3.string(), z3.unknown()).optional()
});
var CallToolRequestSchema = RequestSchema.extend({
  method: z3.literal("tools/call"),
  params: CallToolRequestParamsSchema
});
var ToolListChangedNotificationSchema = NotificationSchema2.extend({
  method: z3.literal("notifications/tools/list_changed"),
  params: NotificationsParamsSchema.optional()
});
var ListChangedOptionsBaseSchema = z3.object({
  /**
   * If true, the list will be refreshed automatically when a list changed notification is received.
   * The callback will be called with the updated list.
   *
   * If false, the callback will be called with null items, allowing manual refresh.
   *
   * @default true
   */
  autoRefresh: z3.boolean().default(true),
  /**
   * Debounce time in milliseconds for list changed notification processing.
   *
   * Multiple notifications received within this timeframe will only trigger one refresh.
   * Set to 0 to disable debouncing.
   *
   * @default 300
   */
  debounceMs: z3.number().int().nonnegative().default(300)
});
var LoggingLevelSchema = z3.enum(["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]);
var SetLevelRequestParamsSchema = BaseRequestParamsSchema.extend({
  /**
   * The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher (i.e., more severe) to the client as notifications/logging/message.
   */
  level: LoggingLevelSchema
});
var SetLevelRequestSchema = RequestSchema.extend({
  method: z3.literal("logging/setLevel"),
  params: SetLevelRequestParamsSchema
});
var LoggingMessageNotificationParamsSchema = NotificationsParamsSchema.extend({
  /**
   * The severity of this log message.
   */
  level: LoggingLevelSchema,
  /**
   * An optional name of the logger issuing this message.
   */
  logger: z3.string().optional(),
  /**
   * The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here.
   */
  data: z3.unknown()
});
var LoggingMessageNotificationSchema = NotificationSchema2.extend({
  method: z3.literal("notifications/message"),
  params: LoggingMessageNotificationParamsSchema
});
var ModelHintSchema = z3.object({
  /**
   * A hint for a model name.
   */
  name: z3.string().optional()
});
var ModelPreferencesSchema = z3.object({
  /**
   * Optional hints to use for model selection.
   */
  hints: z3.array(ModelHintSchema).optional(),
  /**
   * How much to prioritize cost when selecting a model.
   */
  costPriority: z3.number().min(0).max(1).optional(),
  /**
   * How much to prioritize sampling speed (latency) when selecting a model.
   */
  speedPriority: z3.number().min(0).max(1).optional(),
  /**
   * How much to prioritize intelligence and capabilities when selecting a model.
   */
  intelligencePriority: z3.number().min(0).max(1).optional()
});
var ToolChoiceSchema = z3.object({
  /**
   * Controls when tools are used:
   * - "auto": Model decides whether to use tools (default)
   * - "required": Model MUST use at least one tool before completing
   * - "none": Model MUST NOT use any tools
   */
  mode: z3.enum(["auto", "required", "none"]).optional()
});
var ToolResultContentSchema = z3.object({
  type: z3.literal("tool_result"),
  toolUseId: z3.string().describe("The unique identifier for the corresponding tool call."),
  content: z3.array(ContentBlockSchema).default([]),
  structuredContent: z3.object({}).loose().optional(),
  isError: z3.boolean().optional(),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.record(z3.string(), z3.unknown()).optional()
});
var SamplingContentSchema = z3.discriminatedUnion("type", [TextContentSchema, ImageContentSchema, AudioContentSchema]);
var SamplingMessageContentBlockSchema = z3.discriminatedUnion("type", [
  TextContentSchema,
  ImageContentSchema,
  AudioContentSchema,
  ToolUseContentSchema,
  ToolResultContentSchema
]);
var SamplingMessageSchema = z3.object({
  role: RoleSchema,
  content: z3.union([SamplingMessageContentBlockSchema, z3.array(SamplingMessageContentBlockSchema)]),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.record(z3.string(), z3.unknown()).optional()
});
var CreateMessageRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend({
  messages: z3.array(SamplingMessageSchema),
  /**
   * The server's preferences for which model to select. The client MAY modify or omit this request.
   */
  modelPreferences: ModelPreferencesSchema.optional(),
  /**
   * An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt.
   */
  systemPrompt: z3.string().optional(),
  /**
   * A request to include context from one or more MCP servers (including the caller), to be attached to the prompt.
   * The client MAY ignore this request.
   *
   * Default is "none". Values "thisServer" and "allServers" are soft-deprecated. Servers SHOULD only use these values if the client
   * declares ClientCapabilities.sampling.context. These values may be removed in future spec releases.
   */
  includeContext: z3.enum(["none", "thisServer", "allServers"]).optional(),
  temperature: z3.number().optional(),
  /**
   * The requested maximum number of tokens to sample (to prevent runaway completions).
   *
   * The client MAY choose to sample fewer tokens than the requested maximum.
   */
  maxTokens: z3.number().int(),
  stopSequences: z3.array(z3.string()).optional(),
  /**
   * Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific.
   */
  metadata: AssertObjectSchema.optional(),
  /**
   * Tools that the model may use during generation.
   * The client MUST return an error if this field is provided but ClientCapabilities.sampling.tools is not declared.
   */
  tools: z3.array(ToolSchema).optional(),
  /**
   * Controls how the model uses tools.
   * The client MUST return an error if this field is provided but ClientCapabilities.sampling.tools is not declared.
   * Default is `{ mode: "auto" }`.
   */
  toolChoice: ToolChoiceSchema.optional()
});
var CreateMessageRequestSchema = RequestSchema.extend({
  method: z3.literal("sampling/createMessage"),
  params: CreateMessageRequestParamsSchema
});
var CreateMessageResultSchema = ResultSchema.extend({
  /**
   * The name of the model that generated the message.
   */
  model: z3.string(),
  /**
   * The reason why sampling stopped, if known.
   *
   * Standard values:
   * - "endTurn": Natural end of the assistant's turn
   * - "stopSequence": A stop sequence was encountered
   * - "maxTokens": Maximum token limit was reached
   *
   * This field is an open string to allow for provider-specific stop reasons.
   */
  stopReason: z3.optional(z3.enum(["endTurn", "stopSequence", "maxTokens"]).or(z3.string())),
  role: RoleSchema,
  /**
   * Response content. Single content block (text, image, or audio).
   */
  content: SamplingContentSchema
});
var CreateMessageResultWithToolsSchema = ResultSchema.extend({
  /**
   * The name of the model that generated the message.
   */
  model: z3.string(),
  /**
   * The reason why sampling stopped, if known.
   *
   * Standard values:
   * - "endTurn": Natural end of the assistant's turn
   * - "stopSequence": A stop sequence was encountered
   * - "maxTokens": Maximum token limit was reached
   * - "toolUse": The model wants to use one or more tools
   *
   * This field is an open string to allow for provider-specific stop reasons.
   */
  stopReason: z3.optional(z3.enum(["endTurn", "stopSequence", "maxTokens", "toolUse"]).or(z3.string())),
  role: RoleSchema,
  /**
   * Response content. May be a single block or array. May include ToolUseContent if stopReason is "toolUse".
   */
  content: z3.union([SamplingMessageContentBlockSchema, z3.array(SamplingMessageContentBlockSchema)])
});
var BooleanSchemaSchema = z3.object({
  type: z3.literal("boolean"),
  title: z3.string().optional(),
  description: z3.string().optional(),
  default: z3.boolean().optional()
});
var StringSchemaSchema = z3.object({
  type: z3.literal("string"),
  title: z3.string().optional(),
  description: z3.string().optional(),
  minLength: z3.number().optional(),
  maxLength: z3.number().optional(),
  format: z3.enum(["email", "uri", "date", "date-time"]).optional(),
  default: z3.string().optional()
});
var NumberSchemaSchema = z3.object({
  type: z3.enum(["number", "integer"]),
  title: z3.string().optional(),
  description: z3.string().optional(),
  minimum: z3.number().optional(),
  maximum: z3.number().optional(),
  default: z3.number().optional()
});
var UntitledSingleSelectEnumSchemaSchema = z3.object({
  type: z3.literal("string"),
  title: z3.string().optional(),
  description: z3.string().optional(),
  enum: z3.array(z3.string()),
  default: z3.string().optional()
});
var TitledSingleSelectEnumSchemaSchema = z3.object({
  type: z3.literal("string"),
  title: z3.string().optional(),
  description: z3.string().optional(),
  oneOf: z3.array(z3.object({
    const: z3.string(),
    title: z3.string()
  })),
  default: z3.string().optional()
});
var LegacyTitledEnumSchemaSchema = z3.object({
  type: z3.literal("string"),
  title: z3.string().optional(),
  description: z3.string().optional(),
  enum: z3.array(z3.string()),
  enumNames: z3.array(z3.string()).optional(),
  default: z3.string().optional()
});
var SingleSelectEnumSchemaSchema = z3.union([UntitledSingleSelectEnumSchemaSchema, TitledSingleSelectEnumSchemaSchema]);
var UntitledMultiSelectEnumSchemaSchema = z3.object({
  type: z3.literal("array"),
  title: z3.string().optional(),
  description: z3.string().optional(),
  minItems: z3.number().optional(),
  maxItems: z3.number().optional(),
  items: z3.object({
    type: z3.literal("string"),
    enum: z3.array(z3.string())
  }),
  default: z3.array(z3.string()).optional()
});
var TitledMultiSelectEnumSchemaSchema = z3.object({
  type: z3.literal("array"),
  title: z3.string().optional(),
  description: z3.string().optional(),
  minItems: z3.number().optional(),
  maxItems: z3.number().optional(),
  items: z3.object({
    anyOf: z3.array(z3.object({
      const: z3.string(),
      title: z3.string()
    }))
  }),
  default: z3.array(z3.string()).optional()
});
var MultiSelectEnumSchemaSchema = z3.union([UntitledMultiSelectEnumSchemaSchema, TitledMultiSelectEnumSchemaSchema]);
var EnumSchemaSchema = z3.union([LegacyTitledEnumSchemaSchema, SingleSelectEnumSchemaSchema, MultiSelectEnumSchemaSchema]);
var PrimitiveSchemaDefinitionSchema = z3.union([EnumSchemaSchema, BooleanSchemaSchema, StringSchemaSchema, NumberSchemaSchema]);
var ElicitRequestFormParamsSchema = TaskAugmentedRequestParamsSchema.extend({
  /**
   * The elicitation mode.
   *
   * Optional for backward compatibility. Clients MUST treat missing mode as "form".
   */
  mode: z3.literal("form").optional(),
  /**
   * The message to present to the user describing what information is being requested.
   */
  message: z3.string(),
  /**
   * A restricted subset of JSON Schema.
   * Only top-level properties are allowed, without nesting.
   */
  requestedSchema: z3.object({
    type: z3.literal("object"),
    properties: z3.record(z3.string(), PrimitiveSchemaDefinitionSchema),
    required: z3.array(z3.string()).optional()
  })
});
var ElicitRequestURLParamsSchema = TaskAugmentedRequestParamsSchema.extend({
  /**
   * The elicitation mode.
   */
  mode: z3.literal("url"),
  /**
   * The message to present to the user explaining why the interaction is needed.
   */
  message: z3.string(),
  /**
   * The ID of the elicitation, which must be unique within the context of the server.
   * The client MUST treat this ID as an opaque value.
   */
  elicitationId: z3.string(),
  /**
   * The URL that the user should navigate to.
   */
  url: z3.string().url()
});
var ElicitRequestParamsSchema = z3.union([ElicitRequestFormParamsSchema, ElicitRequestURLParamsSchema]);
var ElicitRequestSchema = RequestSchema.extend({
  method: z3.literal("elicitation/create"),
  params: ElicitRequestParamsSchema
});
var ElicitationCompleteNotificationParamsSchema = NotificationsParamsSchema.extend({
  /**
   * The ID of the elicitation that completed.
   */
  elicitationId: z3.string()
});
var ElicitationCompleteNotificationSchema = NotificationSchema2.extend({
  method: z3.literal("notifications/elicitation/complete"),
  params: ElicitationCompleteNotificationParamsSchema
});
var ElicitResultSchema = ResultSchema.extend({
  /**
   * The user action in response to the elicitation.
   * - "accept": User submitted the form/confirmed the action
   * - "decline": User explicitly decline the action
   * - "cancel": User dismissed without making an explicit choice
   */
  action: z3.enum(["accept", "decline", "cancel"]),
  /**
   * The submitted form data, only present when action is "accept".
   * Contains values matching the requested schema.
   * Per MCP spec, content is "typically omitted" for decline/cancel actions.
   * We normalize null to undefined for leniency while maintaining type compatibility.
   */
  content: z3.preprocess((val) => val === null ? void 0 : val, z3.record(z3.string(), z3.union([z3.string(), z3.number(), z3.boolean(), z3.array(z3.string())])).optional())
});
var ResourceTemplateReferenceSchema = z3.object({
  type: z3.literal("ref/resource"),
  /**
   * The URI or URI template of the resource.
   */
  uri: z3.string()
});
var PromptReferenceSchema = z3.object({
  type: z3.literal("ref/prompt"),
  /**
   * The name of the prompt or prompt template
   */
  name: z3.string()
});
var CompleteRequestParamsSchema = BaseRequestParamsSchema.extend({
  ref: z3.union([PromptReferenceSchema, ResourceTemplateReferenceSchema]),
  /**
   * The argument's information
   */
  argument: z3.object({
    /**
     * The name of the argument
     */
    name: z3.string(),
    /**
     * The value of the argument to use for completion matching.
     */
    value: z3.string()
  }),
  context: z3.object({
    /**
     * Previously-resolved variables in a URI template or prompt.
     */
    arguments: z3.record(z3.string(), z3.string()).optional()
  }).optional()
});
var CompleteRequestSchema = RequestSchema.extend({
  method: z3.literal("completion/complete"),
  params: CompleteRequestParamsSchema
});
var CompleteResultSchema = ResultSchema.extend({
  completion: z3.looseObject({
    /**
     * An array of completion values. Must not exceed 100 items.
     */
    values: z3.array(z3.string()).max(100),
    /**
     * The total number of completion options available. This can exceed the number of values actually sent in the response.
     */
    total: z3.optional(z3.number().int()),
    /**
     * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.
     */
    hasMore: z3.optional(z3.boolean())
  })
});
var RootSchema = z3.object({
  /**
   * The URI identifying the root. This *must* start with file:// for now.
   */
  uri: z3.string().startsWith("file://"),
  /**
   * An optional name for the root.
   */
  name: z3.string().optional(),
  /**
   * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
   * for notes on _meta usage.
   */
  _meta: z3.record(z3.string(), z3.unknown()).optional()
});
var ListRootsRequestSchema = RequestSchema.extend({
  method: z3.literal("roots/list"),
  params: BaseRequestParamsSchema.optional()
});
var ListRootsResultSchema = ResultSchema.extend({
  roots: z3.array(RootSchema)
});
var RootsListChangedNotificationSchema = NotificationSchema2.extend({
  method: z3.literal("notifications/roots/list_changed"),
  params: NotificationsParamsSchema.optional()
});
var ClientRequestSchema = z3.union([
  PingRequestSchema,
  InitializeRequestSchema,
  CompleteRequestSchema,
  SetLevelRequestSchema,
  GetPromptRequestSchema,
  ListPromptsRequestSchema,
  ListResourcesRequestSchema,
  ListResourceTemplatesRequestSchema,
  ReadResourceRequestSchema,
  SubscribeRequestSchema,
  UnsubscribeRequestSchema,
  CallToolRequestSchema,
  ListToolsRequestSchema,
  GetTaskRequestSchema2,
  GetTaskPayloadRequestSchema2,
  ListTasksRequestSchema2,
  CancelTaskRequestSchema2
]);
var ClientNotificationSchema = z3.union([
  CancelledNotificationSchema,
  ProgressNotificationSchema,
  InitializedNotificationSchema,
  RootsListChangedNotificationSchema,
  TaskStatusNotificationSchema2
]);
var ClientResultSchema = z3.union([
  EmptyResultSchema2,
  CreateMessageResultSchema,
  CreateMessageResultWithToolsSchema,
  ElicitResultSchema,
  ListRootsResultSchema,
  GetTaskResultSchema2,
  ListTasksResultSchema2,
  CreateTaskResultSchema2
]);
var ServerRequestSchema = z3.union([
  PingRequestSchema,
  CreateMessageRequestSchema,
  ElicitRequestSchema,
  ListRootsRequestSchema,
  GetTaskRequestSchema2,
  GetTaskPayloadRequestSchema2,
  ListTasksRequestSchema2,
  CancelTaskRequestSchema2
]);
var ServerNotificationSchema = z3.union([
  CancelledNotificationSchema,
  ProgressNotificationSchema,
  LoggingMessageNotificationSchema,
  ResourceUpdatedNotificationSchema,
  ResourceListChangedNotificationSchema,
  ToolListChangedNotificationSchema,
  PromptListChangedNotificationSchema,
  TaskStatusNotificationSchema2,
  ElicitationCompleteNotificationSchema
]);
var ServerResultSchema = z3.union([
  EmptyResultSchema2,
  InitializeResultSchema,
  CompleteResultSchema,
  GetPromptResultSchema2,
  ListPromptsResultSchema2,
  ListResourcesResultSchema2,
  ListResourceTemplatesResultSchema2,
  ReadResourceResultSchema2,
  CallToolResultSchema2,
  ListToolsResultSchema2,
  GetTaskResultSchema2,
  ListTasksResultSchema2,
  CreateTaskResultSchema2
]);

// ../../node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
import * as z3rt from "zod/v3";
import * as z4mini from "zod/v4-mini";

// ../../node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
import * as z4mini2 from "zod/v4-mini";

// ../../node_modules/zod-to-json-schema/dist/esm/selectParser.js
import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind3 } from "zod/v3";

// ../../node_modules/zod-to-json-schema/dist/esm/parsers/array.js
import { ZodFirstPartyTypeKind } from "zod/v3";

// ../../node_modules/zod-to-json-schema/dist/esm/parsers/record.js
import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind2 } from "zod/v3";

// ../../node_modules/zod-to-json-schema/dist/esm/parsers/string.js
var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");

// ../../node_modules/@modelcontextprotocol/ext-apps/dist/src/app-bridge.js
import { z as Q } from "zod/v4";
import { z as mQ } from "zod/v4";
var ZQ = ((X) => typeof __require < "u" ? __require : typeof Proxy < "u" ? new Proxy(X, { get: (Y, Z) => (typeof __require < "u" ? __require : Y)[Z] }) : X)(function(X) {
  if (typeof __require < "u") return __require.apply(this, arguments);
  throw Error('Dynamic require of "' + X + '" is not supported');
});
var r = Q.union([Q.literal("light"), Q.literal("dark")]).describe("Color theme preference for the host environment.");
var B = Q.union([Q.literal("inline"), Q.literal("fullscreen"), Q.literal("pip")]).describe("Display mode for UI presentation.");
var HQ = Q.union([Q.literal("--color-background-primary"), Q.literal("--color-background-secondary"), Q.literal("--color-background-tertiary"), Q.literal("--color-background-inverse"), Q.literal("--color-background-ghost"), Q.literal("--color-background-info"), Q.literal("--color-background-danger"), Q.literal("--color-background-success"), Q.literal("--color-background-warning"), Q.literal("--color-background-disabled"), Q.literal("--color-text-primary"), Q.literal("--color-text-secondary"), Q.literal("--color-text-tertiary"), Q.literal("--color-text-inverse"), Q.literal("--color-text-ghost"), Q.literal("--color-text-info"), Q.literal("--color-text-danger"), Q.literal("--color-text-success"), Q.literal("--color-text-warning"), Q.literal("--color-text-disabled"), Q.literal("--color-border-primary"), Q.literal("--color-border-secondary"), Q.literal("--color-border-tertiary"), Q.literal("--color-border-inverse"), Q.literal("--color-border-ghost"), Q.literal("--color-border-info"), Q.literal("--color-border-danger"), Q.literal("--color-border-success"), Q.literal("--color-border-warning"), Q.literal("--color-border-disabled"), Q.literal("--color-ring-primary"), Q.literal("--color-ring-secondary"), Q.literal("--color-ring-inverse"), Q.literal("--color-ring-info"), Q.literal("--color-ring-danger"), Q.literal("--color-ring-success"), Q.literal("--color-ring-warning"), Q.literal("--font-sans"), Q.literal("--font-mono"), Q.literal("--font-weight-normal"), Q.literal("--font-weight-medium"), Q.literal("--font-weight-semibold"), Q.literal("--font-weight-bold"), Q.literal("--font-text-xs-size"), Q.literal("--font-text-sm-size"), Q.literal("--font-text-md-size"), Q.literal("--font-text-lg-size"), Q.literal("--font-heading-xs-size"), Q.literal("--font-heading-sm-size"), Q.literal("--font-heading-md-size"), Q.literal("--font-heading-lg-size"), Q.literal("--font-heading-xl-size"), Q.literal("--font-heading-2xl-size"), Q.literal("--font-heading-3xl-size"), Q.literal("--font-text-xs-line-height"), Q.literal("--font-text-sm-line-height"), Q.literal("--font-text-md-line-height"), Q.literal("--font-text-lg-line-height"), Q.literal("--font-heading-xs-line-height"), Q.literal("--font-heading-sm-line-height"), Q.literal("--font-heading-md-line-height"), Q.literal("--font-heading-lg-line-height"), Q.literal("--font-heading-xl-line-height"), Q.literal("--font-heading-2xl-line-height"), Q.literal("--font-heading-3xl-line-height"), Q.literal("--border-radius-xs"), Q.literal("--border-radius-sm"), Q.literal("--border-radius-md"), Q.literal("--border-radius-lg"), Q.literal("--border-radius-xl"), Q.literal("--border-radius-full"), Q.literal("--border-width-regular"), Q.literal("--shadow-hairline"), Q.literal("--shadow-sm"), Q.literal("--shadow-md"), Q.literal("--shadow-lg")]).describe("CSS variable keys available to MCP apps for theming.");
var RQ = Q.record(HQ.describe(`Style variables for theming MCP apps.

Individual style keys are optional - hosts may provide any subset of these values.
Values are strings containing CSS values (colors, sizes, font stacks, etc.).

Note: This type uses \`Record<K, string | undefined>\` rather than \`Partial<Record<K, string>>\`
for compatibility with Zod schema generation. Both are functionally equivalent for validation.`), Q.union([Q.string(), Q.undefined()]).describe(`Style variables for theming MCP apps.

Individual style keys are optional - hosts may provide any subset of these values.
Values are strings containing CSS values (colors, sizes, font stacks, etc.).

Note: This type uses \`Record<K, string | undefined>\` rather than \`Partial<Record<K, string>>\`
for compatibility with Zod schema generation. Both are functionally equivalent for validation.`)).describe(`Style variables for theming MCP apps.

Individual style keys are optional - hosts may provide any subset of these values.
Values are strings containing CSS values (colors, sizes, font stacks, etc.).

Note: This type uses \`Record<K, string | undefined>\` rather than \`Partial<Record<K, string>>\`
for compatibility with Zod schema generation. Both are functionally equivalent for validation.`);
var L = Q.object({ method: Q.literal("ui/open-link"), params: Q.object({ url: Q.string().describe("URL to open in the host's browser") }) });
var P = Q.object({ isError: Q.boolean().optional().describe("True if the host failed to open the URL (e.g., due to security policy).") }).passthrough();
var A = Q.object({ isError: Q.boolean().optional().describe("True if the download failed (e.g., user cancelled or host denied).") }).passthrough();
var U = Q.object({ isError: Q.boolean().optional().describe("True if the host rejected or failed to deliver the message.") }).passthrough();
var w = Q.object({ method: Q.literal("ui/notifications/sandbox-proxy-ready"), params: Q.object({}) });
var O = Q.object({ connectDomains: Q.array(Q.string()).optional().describe(`Origins for network requests (fetch/XHR/WebSocket).

- Maps to CSP \`connect-src\` directive
- Empty or omitted \u2192 no network connections (secure default)`), resourceDomains: Q.array(Q.string()).optional().describe("Origins for static resources (images, scripts, stylesheets, fonts, media).\n\n- Maps to CSP `img-src`, `script-src`, `style-src`, `font-src`, `media-src` directives\n- Wildcard subdomains supported: `https://*.example.com`\n- Empty or omitted \u2192 no network resources (secure default)"), frameDomains: Q.array(Q.string()).optional().describe("Origins for nested iframes.\n\n- Maps to CSP `frame-src` directive\n- Empty or omitted \u2192 no nested iframes allowed (`frame-src 'none'`)"), baseUriDomains: Q.array(Q.string()).optional().describe("Allowed base URIs for the document.\n\n- Maps to CSP `base-uri` directive\n- Empty or omitted \u2192 only same origin allowed (`base-uri 'self'`)") });
var z4 = Q.object({ camera: Q.object({}).optional().describe("Request camera access.\n\nMaps to Permission Policy `camera` feature."), microphone: Q.object({}).optional().describe("Request microphone access.\n\nMaps to Permission Policy `microphone` feature."), geolocation: Q.object({}).optional().describe("Request geolocation access.\n\nMaps to Permission Policy `geolocation` feature."), clipboardWrite: Q.object({}).optional().describe("Request clipboard write access.\n\nMaps to Permission Policy `clipboard-write` feature.") });
var H = Q.object({ method: Q.literal("ui/notifications/size-changed"), params: Q.object({ width: Q.number().optional().describe("New width in pixels."), height: Q.number().optional().describe("New height in pixels.") }) });
var R = Q.object({ method: Q.literal("ui/notifications/tool-input"), params: Q.object({ arguments: Q.record(Q.string(), Q.unknown().describe("Complete tool call arguments as key-value pairs.")).optional().describe("Complete tool call arguments as key-value pairs.") }) });
var T = Q.object({ method: Q.literal("ui/notifications/tool-input-partial"), params: Q.object({ arguments: Q.record(Q.string(), Q.unknown().describe("Partial tool call arguments (incomplete, may change).")).optional().describe("Partial tool call arguments (incomplete, may change).") }) });
var M = Q.object({ method: Q.literal("ui/notifications/tool-cancelled"), params: Q.object({ reason: Q.string().optional().describe('Optional reason for the cancellation (e.g., "user action", "timeout").') }) });
var o = Q.object({ fonts: Q.string().optional() });
var s = Q.object({ variables: RQ.optional().describe("CSS variables for theming the app."), css: o.optional().describe("CSS blocks that apps can inject.") });
var C = Q.object({ method: Q.literal("ui/resource-teardown"), params: Q.object({}) });
var S = Q.record(Q.string(), Q.unknown());
var F = Q.object({ text: Q.object({}).optional().describe("Host supports text content blocks."), image: Q.object({}).optional().describe("Host supports image content blocks."), audio: Q.object({}).optional().describe("Host supports audio content blocks."), resource: Q.object({}).optional().describe("Host supports resource content blocks."), resourceLink: Q.object({}).optional().describe("Host supports resource link content blocks."), structuredContent: Q.object({}).optional().describe("Host supports structured content.") });
var q = Q.object({ method: Q.literal("ui/notifications/request-teardown"), params: Q.object({}).optional() });
var t = Q.object({ experimental: Q.object({}).optional().describe("Experimental features (structure TBD)."), openLinks: Q.object({}).optional().describe("Host supports opening external URLs."), downloadFile: Q.object({}).optional().describe("Host supports file downloads via ui/download-file."), serverTools: Q.object({ listChanged: Q.boolean().optional().describe("Host supports tools/list_changed notifications.") }).optional().describe("Host can proxy tool calls to the MCP server."), serverResources: Q.object({ listChanged: Q.boolean().optional().describe("Host supports resources/list_changed notifications.") }).optional().describe("Host can proxy resource reads to the MCP server."), logging: Q.object({}).optional().describe("Host accepts log messages."), sandbox: Q.object({ permissions: z4.optional().describe("Permissions granted by the host (camera, microphone, geolocation)."), csp: O.optional().describe("CSP domains approved by the host.") }).optional().describe("Sandbox configuration applied by the host."), updateModelContext: F.optional().describe("Host accepts context updates (ui/update-model-context) to be included in the model's context for future turns."), message: F.optional().describe("Host supports receiving content messages (ui/message) from the view."), sampling: Q.object({ tools: Q.object({}).optional().describe("Host supports tool use via `tools` and `toolChoice` parameters.") }).optional().describe("Host supports LLM sampling (sampling/createMessage) from the view.\nMirrors the MCP `ClientCapabilities.sampling` shape so hosts can pass it through.") });
var a = Q.object({ experimental: Q.object({}).optional().describe("Experimental features (structure TBD)."), tools: Q.object({ listChanged: Q.boolean().optional().describe("App supports tools/list_changed notifications.") }).optional().describe("App exposes MCP-style tools that the host can call."), availableDisplayModes: Q.array(B).optional().describe("Display modes the app supports.") });
var y = Q.object({ method: Q.literal("ui/notifications/initialized"), params: Q.object({}).optional() });
var TQ = Q.object({ csp: O.optional().describe("Content Security Policy configuration for UI resources."), permissions: z4.optional().describe("Sandbox permissions requested by the UI resource."), domain: Q.string().optional().describe(`Dedicated origin for view sandbox.

Useful when views need stable, dedicated origins for OAuth callbacks, CORS policies, or API key allowlists.

**Host-dependent:** The format and validation rules for this field are determined by each host. Servers MUST consult host-specific documentation for the expected domain format. Common patterns include:
- Hash-based subdomains (e.g., \`{hash}.claudemcpcontent.com\`)
- URL-derived subdomains (e.g., \`www-example-com.oaiusercontent.com\`)

If omitted, host uses default sandbox origin (typically per-conversation).`), prefersBorder: Q.boolean().optional().describe(`Visual boundary preference - true if view prefers a visible border.

Boolean requesting whether a visible border and background is provided by the host. Specifying an explicit value for this is recommended because hosts' defaults may vary.

- \`true\`: request visible border + background
- \`false\`: request no visible border + background
- omitted: host decides border`) });
var I = Q.object({ method: Q.literal("ui/request-display-mode"), params: Q.object({ mode: B.describe("The display mode being requested.") }) });
var g = Q.object({ mode: B.describe("The display mode that was actually set. May differ from requested if not supported.") }).passthrough();
var e = Q.union([Q.literal("model"), Q.literal("app")]).describe("Tool visibility scope - who can access the tool.");
var MQ = Q.object({ resourceUri: Q.string().optional(), visibility: Q.array(e).optional().describe(`Who can access this tool. Default: ["model", "app"]
- "model": Tool visible to and callable by the agent
- "app": Tool callable by the app from this server only`), csp: Q.never().optional(), permissions: Q.never().optional() });
var OX = Q.object({ mimeTypes: Q.array(Q.string()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.') });
var f = Q.object({ method: Q.literal("ui/download-file"), params: Q.object({ contents: Q.array(Q.union([EmbeddedResourceSchema, ResourceLinkSchema])).describe("Resource contents to download \u2014 embedded (inline data) or linked (host fetches). Uses standard MCP resource types.") }) });
var k = Q.object({ method: Q.literal("ui/message"), params: Q.object({ role: Q.literal("user").describe('Message role, currently only "user" is supported.'), content: Q.array(ContentBlockSchema).describe("Message content blocks (text, image, etc.).") }) });
var CQ = Q.object({ method: Q.literal("ui/notifications/sandbox-resource-ready"), params: Q.object({ html: Q.string().describe("HTML content to load into the inner iframe."), sandbox: Q.string().optional().describe("Optional override for the inner iframe's sandbox attribute."), csp: O.optional().describe("CSP configuration from resource metadata."), permissions: z4.optional().describe("Sandbox permissions from resource metadata.") }) });
var v = Q.object({ method: Q.literal("ui/notifications/tool-result"), params: CallToolResultSchema2.describe("Standard MCP tool execution result.") });
var x = Q.object({ toolInfo: Q.object({ id: RequestIdSchema.optional().describe("JSON-RPC id of the tools/call request."), tool: ToolSchema.describe("Tool definition including name, inputSchema, etc.") }).optional().describe("Metadata of the tool call that instantiated this App."), theme: r.optional().describe("Current color theme preference."), styles: s.optional().describe("Style configuration for theming the app."), displayMode: B.optional().describe("How the UI is currently displayed."), availableDisplayModes: Q.array(B).optional().describe("Display modes the host supports."), containerDimensions: Q.union([Q.object({ height: Q.number().describe("Fixed container height in pixels.") }), Q.object({ maxHeight: Q.union([Q.number(), Q.undefined()]).optional().describe("Maximum container height in pixels.") })]).and(Q.union([Q.object({ width: Q.number().describe("Fixed container width in pixels.") }), Q.object({ maxWidth: Q.union([Q.number(), Q.undefined()]).optional().describe("Maximum container width in pixels.") })])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
container holding the app. Specify either width or maxWidth, and either height or maxHeight.`), locale: Q.string().optional().describe("User's language and region preference in BCP 47 format."), timeZone: Q.string().optional().describe("User's timezone in IANA format."), userAgent: Q.string().optional().describe("Host application identifier."), platform: Q.union([Q.literal("web"), Q.literal("desktop"), Q.literal("mobile")]).optional().describe("Platform type for responsive design decisions."), deviceCapabilities: Q.object({ touch: Q.boolean().optional().describe("Whether the device supports touch input."), hover: Q.boolean().optional().describe("Whether the device supports hover interactions.") }).optional().describe("Device input capabilities."), safeAreaInsets: Q.object({ top: Q.number().describe("Top safe area inset in pixels."), right: Q.number().describe("Right safe area inset in pixels."), bottom: Q.number().describe("Bottom safe area inset in pixels."), left: Q.number().describe("Left safe area inset in pixels.") }).optional().describe("Mobile safe area boundaries in pixels.") }).passthrough();
var b = Q.object({ method: Q.literal("ui/notifications/host-context-changed"), params: x.describe("Partial context update containing only changed fields.") });
var d = Q.object({ method: Q.literal("ui/update-model-context"), params: Q.object({ content: Q.array(ContentBlockSchema).optional().describe("Context content blocks (text, image, etc.)."), structuredContent: Q.record(Q.string(), Q.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.") }) });
var u = Q.object({ method: Q.literal("ui/initialize"), params: Q.object({ appInfo: ImplementationSchema.describe("App identification (name and version)."), appCapabilities: a.describe("Features and capabilities this app provides."), protocolVersion: Q.string().describe("Protocol version this app supports.") }) });
var h = Q.object({ protocolVersion: Q.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'), hostInfo: ImplementationSchema.describe("Host application identification and version."), hostCapabilities: t.describe("Features and capabilities provided by the host."), hostContext: x.describe("Rich context about the host environment.") }).passthrough();
var i = "ui/resourceUri";
function FZ(X) {
  let Z = X._meta?.ui?.resourceUri;
  if (Z === void 0) Z = X._meta?.[i];
  if (typeof Z === "string" && Z.startsWith("ui://")) return Z;
  else if (Z !== void 0) throw Error(`Invalid UI resource URI: ${JSON.stringify(Z)}`);
  return;
}

// ../../core/mcp/apps.ts
function getAppResourceUri(tool) {
  return FZ(
    tool
  );
}
function readUiMeta(carrier) {
  const ui = carrier !== null && typeof carrier === "object" ? carrier._meta?.ui : void 0;
  return ui !== null && typeof ui === "object" ? ui : void 0;
}
function normalizeResourceUri(uri) {
  const lowered = uri.toLowerCase();
  return lowered.endsWith("/") ? lowered.slice(0, -1) : lowered;
}
function findResourceContent(resource, requestedUri) {
  const exact = resource.contents.find((c) => c.uri === requestedUri);
  if (exact) return exact;
  const norm = normalizeResourceUri(requestedUri);
  const normalized = resource.contents.find(
    (c) => normalizeResourceUri(c.uri) === norm
  );
  if (normalized) return normalized;
  return resource.contents.length === 1 ? resource.contents[0] : void 0;
}
function extractAppInfo(tool, resource) {
  const resourceUri = getAppResourceUri(tool);
  if (resourceUri === void 0) {
    return { hasApp: false, toolName: tool.name };
  }
  const toolUi = readUiMeta(tool);
  const content = resource && findResourceContent(resource, resourceUri);
  const resourceUi = readUiMeta(content) ?? readUiMeta(resource);
  return {
    hasApp: true,
    toolName: tool.name,
    resourceUri,
    ...toolUi?.visibility ? { visibility: toolUi.visibility } : {},
    ...resourceUi?.csp ? { csp: resourceUi.csp } : {},
    ...resourceUi?.permissions ? { permissions: resourceUi.permissions } : {},
    ...resourceUi?.domain ? { domain: resourceUi.domain } : {},
    ...resourceUi?.prefersBorder !== void 0 ? { prefersBorder: resourceUi.prefersBorder } : {},
    ...typeof content?.mimeType === "string" ? { resourceMimeType: content.mimeType } : {}
  };
}

// src/handlers/collect-app-info.ts
async function collectAppInfo(client, tool, metadata) {
  let base;
  try {
    base = extractAppInfo(tool);
  } catch (e2) {
    return {
      hasApp: false,
      toolName: tool.name,
      resourceError: e2 instanceof Error ? e2.message : String(e2)
    };
  }
  if (!base.hasApp || base.resourceUri === void 0) return base;
  try {
    const read = await client.readResource(base.resourceUri, metadata);
    return extractAppInfo(tool, read.result);
  } catch (e2) {
    return {
      ...base,
      resourceError: e2 instanceof Error ? e2.message : String(e2)
    };
  }
}

// src/handlers/run-method.ts
async function runMethod(inspectorClient, args) {
  let managedToolsState = null;
  let managedResourcesState = null;
  let managedResourceTemplatesState = null;
  let managedPromptsState = null;
  let managedTasksState = null;
  try {
    let result;
    let appInfo;
    if (args.method === "tools/list" || args.method === "tools/call") {
      managedToolsState = new ManagedToolsState(inspectorClient);
      managedToolsState.setMetadata(args.metadata);
      await managedToolsState.refresh();
    }
    if (args.method === "resources/list") {
      managedResourcesState = new ManagedResourcesState(inspectorClient);
      managedResourcesState.setMetadata(args.metadata);
      await managedResourcesState.refresh();
    } else if (args.method === "resources/templates/list") {
      managedResourceTemplatesState = new ManagedResourceTemplatesState(
        inspectorClient
      );
      managedResourceTemplatesState.setMetadata(args.metadata);
      await managedResourceTemplatesState.refresh();
    } else if (args.method === "prompts/list") {
      managedPromptsState = new ManagedPromptsState(inspectorClient);
      managedPromptsState.setMetadata(args.metadata);
      await managedPromptsState.refresh();
    } else if (args.method === "tasks/list") {
      managedTasksState = new ManagedRequestorTasksState(inspectorClient);
      await managedTasksState.refresh();
    }
    if (args.method === "tools/list") {
      const tools = managedToolsState.getTools();
      if (args.appInfo) {
        const lines = [];
        for (const tool of tools) {
          lines.push(
            await collectAppInfo(inspectorClient, tool, args.metadata)
          );
        }
        return { kind: "ndjson", lines };
      }
      result = { tools };
    } else if (args.method === "tools/call") {
      if (!args.toolName) {
        throw new Error(
          "Tool name is required for tools/call method. Use --tool-name to specify the tool name."
        );
      }
      const tool = managedToolsState.getTools().find((t2) => t2.name === args.toolName);
      if (!tool) {
        throw new CliExitCodeError(
          EXIT_CODES.TOOL_ERROR,
          `Tool '${args.toolName}' not found on server.`,
          { code: "tool_not_found" }
        );
      }
      if (args.appInfo || args.format === "json") {
        appInfo = await collectAppInfo(inspectorClient, tool, args.metadata);
      }
      if (args.appInfo) {
        result = { ...appInfo };
      } else {
        const invocation = args.task ? await inspectorClient.callToolStream(
          tool,
          args.toolArg || {},
          args.metadata,
          args.toolMeta
        ) : await inspectorClient.callTool(
          tool,
          args.toolArg || {},
          args.metadata,
          args.toolMeta
        );
        if (invocation.result !== null) {
          result = invocation.result;
        } else {
          result = {
            content: [
              {
                type: "text",
                text: invocation.error || "Tool call failed"
              }
            ],
            isError: true
          };
        }
      }
    } else if (args.method === "resources/list") {
      result = { resources: managedResourcesState.getResources() };
    } else if (args.method === "resources/read") {
      if (!args.uri) {
        throw new Error(
          "URI is required for resources/read method. Use --uri to specify the resource URI."
        );
      }
      const invocation = await inspectorClient.readResource(
        args.uri,
        args.metadata
      );
      result = invocation.result;
    } else if (args.method === "resources/templates/list") {
      result = {
        resourceTemplates: managedResourceTemplatesState.getResourceTemplates()
      };
    } else if (args.method === "resources/subscribe") {
      if (!args.uri) {
        throw new Error(
          "URI is required for resources/subscribe. Use --uri to specify the resource URI."
        );
      }
      await inspectorClient.subscribeToResource(args.uri);
      return {
        kind: "stream",
        label: "resources/subscribe",
        start: (writeLine) => {
          writeLine({ type: "subscribed", uri: args.uri });
          const onUpdate = (ev) => {
            const detail = ev.detail;
            writeLine({
              type: "resources/updated",
              uri: detail?.uri ?? args.uri
            });
          };
          inspectorClient.addEventListener("resourceUpdated", onUpdate);
          return () => {
            inspectorClient.removeEventListener("resourceUpdated", onUpdate);
            void inspectorClient.unsubscribeFromResource(args.uri);
          };
        }
      };
    } else if (args.method === "resources/unsubscribe") {
      if (!args.uri) {
        throw new Error(
          "URI is required for resources/unsubscribe. Use --uri to specify the resource URI."
        );
      }
      await inspectorClient.unsubscribeFromResource(args.uri);
      result = { unsubscribed: true, uri: args.uri };
    } else if (args.method === "prompts/list") {
      result = { prompts: managedPromptsState.getPrompts() };
    } else if (args.method === "prompts/get") {
      if (!args.promptName) {
        throw new Error(
          "Prompt name is required for prompts/get method. Use --prompt-name to specify the prompt name."
        );
      }
      const invocation = await inspectorClient.getPrompt(
        args.promptName,
        args.promptArgs || {},
        args.metadata
      );
      result = invocation.result;
    } else if (args.method === "prompts/complete") {
      if (!args.completeRefType || !args.completeRef || !args.completeArgName) {
        throw new Error(
          "prompts/complete requires --complete-ref-type, --complete-ref, and --complete-arg-name."
        );
      }
      const ref = args.completeRefType === "ref/prompt" ? { type: "ref/prompt", name: args.completeRef } : { type: "ref/resource", uri: args.completeRef };
      result = await inspectorClient.getCompletions(
        ref,
        args.completeArgName,
        args.completeArgValue ?? "",
        void 0,
        args.metadata
      );
    } else if (args.method === "initialize") {
      result = {
        serverInfo: inspectorClient.getServerInfo(),
        protocolVersion: inspectorClient.getProtocolVersion(),
        capabilities: inspectorClient.getCapabilities(),
        instructions: inspectorClient.getInstructions()
      };
    } else if (args.method === "logging/setLevel") {
      if (!args.logLevel) {
        throw new Error(
          "Log level is required for logging/setLevel method. Use --log-level to specify the log level."
        );
      }
      await inspectorClient.setLoggingLevel(args.logLevel);
      result = {};
    } else if (args.method === "logging/tail") {
      return {
        kind: "stream",
        label: "logging/tail",
        start: (writeLine) => {
          const log = new MessageLogState(inspectorClient);
          const onMessage = (ev) => {
            const entry = ev.detail;
            if (entry.direction === "notification" && entry.message?.method === "notifications/message") {
              writeLine(entry);
            }
          };
          log.addEventListener("message", onMessage);
          return () => {
            log.removeEventListener("message", onMessage);
            log.destroy();
          };
        }
      };
    } else if (args.method === "tasks/list") {
      result = { tasks: managedTasksState.getTasks() };
    } else if (args.method === "tasks/get") {
      if (!args.taskId) {
        throw new Error("Task id is required for tasks/get. Use --task-id.");
      }
      result = {
        task: await inspectorClient.getRequestorTask(args.taskId)
      };
    } else if (args.method === "tasks/cancel") {
      if (!args.taskId) {
        throw new Error("Task id is required for tasks/cancel. Use --task-id.");
      }
      await inspectorClient.cancelRequestorTask(args.taskId);
      result = { cancelled: true, taskId: args.taskId };
    } else if (args.method === "tasks/result") {
      if (!args.taskId) {
        throw new Error("Task id is required for tasks/result. Use --task-id.");
      }
      result = await inspectorClient.getRequestorTaskResult(
        args.taskId
      );
    } else if (args.method === "roots/list") {
      result = { roots: inspectorClient.getRoots() };
    } else if (args.method === "roots/set") {
      if (!args.rootsJson) {
        throw new Error(
          "roots/set requires --roots-json '<json array of {uri,name?}>'."
        );
      }
      let roots;
      try {
        const parsed = JSON.parse(args.rootsJson);
        if (!Array.isArray(parsed)) {
          throw new Error("must be a JSON array");
        }
        roots = parsed;
      } catch (e2) {
        throw new Error(
          `--roots-json is invalid: ${e2 instanceof Error ? e2.message : String(e2)}`
        );
      }
      await inspectorClient.setRoots(roots);
      result = { roots: inspectorClient.getRoots() };
    } else {
      throw new Error(
        `Unsupported method: ${args.method}. Supported methods include: initialize, tools/*, resources/*, prompts/*, logging/*, tasks/*, roots/*.`
      );
    }
    return { kind: "result", result, appInfo };
  } finally {
    managedToolsState?.destroy();
    managedResourcesState?.destroy();
    managedResourceTemplatesState?.destroy();
    managedPromptsState?.destroy();
    managedTasksState?.destroy();
  }
}

// src/handlers/method-types.ts
var ONE_SHOT_METHODS = [
  "initialize",
  "tools/list",
  "tools/call",
  "resources/list",
  "resources/read",
  "resources/templates/list",
  "prompts/list",
  "prompts/get",
  "logging/setLevel"
];
function isOneShotMethod(method) {
  return ONE_SHOT_METHODS.includes(method);
}
function metaValueToString(value) {
  if (typeof value === "string") return value;
  return JSON.stringify(value);
}

// src/cli.ts
import {
  refreshAuthorization,
  discoverAuthorizationServerMetadata as discoverAuthorizationServerMetadata5
} from "@modelcontextprotocol/client";

// ../../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/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(s2) {
  return s2.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: path2 = DEFAULT_CALLBACK_PATH,
      onCallback,
      onError
    } = options;
    if (!path2.startsWith("/")) {
      return Promise.reject(
        new Error("Callback path must start with '/' (absolute path)")
      );
    }
    this.onCallback = onCallback;
    this.onError = onError;
    this.handled = false;
    this.callbackPath = path2;
    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 a2 = this.server.address();
        if (!a2 || typeof a2 === "string") {
          reject(new Error("Failed to get server address"));
          return;
        }
        this.port = a2.port;
        resolve4({
          port: this.port,
          redirectUrl: buildRedirectUrl(hostname, this.port, path2)
        });
      });
    });
  }
  /**
   * 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 u2 = 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 = u2.pathname;
      search = u2.search;
      state = u2.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, path2) {
  return `http://${formatHostForUrl(host)}:${port}${path2}`;
}

// ../../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}`);
  }
  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}`;
}

// ../../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/open-url.ts
import open from "open";
async function openUrl(url) {
  await open(typeof url === "string" ? url : url.href);
}

// src/style.ts
var identity = (s2) => s2;
function sgr(enabled, open2, close) {
  if (!enabled) return identity;
  return (s2) => `\x1B[${open2}m${s2}\x1B[${close}m`;
}
function createStyle(ansi) {
  return {
    ansi,
    bold: sgr(ansi, 1, 22),
    dim: sgr(ansi, 2, 22),
    red: sgr(ansi, 31, 39),
    yellow: sgr(ansi, 33, 39),
    green: sgr(ansi, 32, 39),
    cyan: sgr(ansi, 36, 39),
    link: (uri, label) => {
      const text = label ?? uri;
      if (!ansi || !uri) return text;
      return `\x1B]8;;${uri}\x07${text}\x1B]8;;\x07`;
    }
  };
}
var PLAIN = createStyle(false);
function resolveAnsiEnabled(opts = {}) {
  if (opts.plain) return false;
  if (opts.format === "json") return false;
  const noColor = opts.noColorEnv !== void 0 ? opts.noColorEnv : process.env.NO_COLOR;
  if (noColor !== void 0 && noColor !== "") return false;
  const tty = opts.isTTY !== void 0 ? opts.isTTY : process.stdout.isTTY === true;
  return tty;
}

// src/cli-oauth-navigation.ts
function resolveCliAutoOpenEnabled(env = process.env) {
  const flag = env.MCP_AUTO_OPEN_ENABLED;
  if (flag === "true") return true;
  if (flag === "false") return false;
  return !env.VITEST;
}
function isCliAutoOpenForced(env = process.env) {
  return env.MCP_AUTO_OPEN_ENABLED === "true";
}
function createCliOAuthNavigation(options = {}) {
  return new CallbackNavigation(async (url) => {
    const armed = options.autoOpenControl?.armed === true;
    if (options.disableAutoOpen || !armed) return;
    const href = url.href;
    const tty = options.isTTY !== void 0 ? options.isTTY : process.stderr.isTTY === true;
    const style = createStyle(
      resolveAnsiEnabled({
        isTTY: tty,
        noColorEnv: options.noColorEnv
      })
    );
    const write = options.write ?? ((line) => process.stderr.write(line));
    write(`Please navigate to: ${style.link(href)}
`);
    const envAllows = options.autoOpenEnabled !== void 0 ? options.autoOpenEnabled : resolveCliAutoOpenEnabled();
    if (!envAllows) return;
    const forced = options.forceAutoOpen ?? isCliAutoOpenForced();
    if (!forced && !tty) return;
    try {
      await (options.openBrowser ?? openUrl)(href);
    } catch {
    }
  });
}

// ../../core/client/config-parse.ts
import { z as z5 } 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 = z5.string().min(1).superRefine((val, ctx) => {
  const trimmed = val.trim();
  if (!isAbsoluteHttpUrl(trimmed)) {
    ctx.addIssue({
      code: z5.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: z5.ZodIssueCode.custom,
        message: "CIMD client metadata URL is required when CIMD is enabled"
      });
    }
    return;
  }
  const error = getCimdClientMetadataUrlError(trimmed);
  if (error) {
    ctx.addIssue({
      code: z5.ZodIssueCode.custom,
      message: error
    });
  }
}
var CimdConfigSchema = z5.object({
  enabled: z5.boolean(),
  clientMetadataUrl: z5.string()
}).superRefine((data, ctx) => {
  refineCimdMetadataUrl(data.clientMetadataUrl, ctx, data.enabled === true);
});
var EnterpriseManagedAuthIdpConfigSchema = z5.object({
  issuer: HttpUrlStringSchema,
  clientId: z5.string().min(1),
  clientSecret: z5.string().optional()
});
var ClientConfigSchema = z5.object({
  enterpriseManagedAuth: z5.object({
    enabled: z5.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 }
  };
}

// src/cliOAuth.ts
import { createInterface } from "readline/promises";
var STEP_UP_PIPE_TIMEOUT_MS = 5e3;
function authRequiredFailure(message) {
  throw new CliExitCodeError(EXIT_CODES.AUTH_REQUIRED, message, {
    code: "auth_required"
  });
}
function assertInteractiveOAuthAllowed(options) {
  const humanPresent = options?.isTTY !== void 0 ? options.isTTY : process.stdin.isTTY === true || process.stderr.isTTY === true;
  if (humanPresent || isCliAutoOpenForced()) return;
  authRequiredFailure(
    "Interactive OAuth requires a TTY on stdin or stderr (or MCP_AUTO_OPEN_ENABLED=true). For CI/non-interactive runs use --stored-auth-only."
  );
}
function isStandardOAuthStepUp2(challenge, settings) {
  return isStandardOAuthStepUp(challenge, {
    enterpriseManaged: settings?.enterpriseManaged
  });
}
async function confirmStepUpFromStdin(timeoutMs) {
  const rl = createInterface({ input: process.stdin, output: process.stderr });
  const applyTimeoutMs = timeoutMs !== void 0 ? timeoutMs : process.stdin.isTTY === true ? void 0 : STEP_UP_PIPE_TIMEOUT_MS;
  try {
    const answer = await new Promise((resolve4, reject) => {
      let settled = false;
      let lastLine;
      let timer;
      function cleanup() {
        if (timer !== void 0) clearTimeout(timer);
        rl.removeListener("close", onClose);
        rl.removeListener("line", onLine);
      }
      function finish(value) {
        if (settled) return;
        settled = true;
        cleanup();
        resolve4(value);
      }
      function finishTimeout() {
        if (settled) return;
        settled = true;
        cleanup();
        reject(
          new CliExitCodeError(
            EXIT_CODES.AUTH_REQUIRED,
            `Step-up authorization timed out (no answer on stdin within ${String(applyTimeoutMs)}ms). For CI/non-interactive runs use --stored-auth-only.`,
            { code: "auth_required" }
          )
        );
      }
      function onLine(line) {
        lastLine = line;
      }
      function onClose() {
        finish(lastLine ?? "n");
      }
      rl.on("line", onLine);
      rl.once("close", onClose);
      if (applyTimeoutMs !== void 0) {
        timer = setTimeout(finishTimeout, applyTimeoutMs);
      }
      void rl.question("").then(
        (line) => finish(line),
        () => finish(lastLine ?? "n")
      );
    });
    const normalized = answer.trim().toLowerCase();
    return normalized === "y" || normalized === "yes";
  } finally {
    rl.close();
  }
}
async function promptStepUpConfirm(challenge, confirmStepUp) {
  process.stderr.write(`${stepUpConfirmMessage(challenge)}
`);
  process.stderr.write("Proceed with step-up authorization? [y/N] ");
  return confirmStepUp();
}
async function withArmedAutoOpen(control, fn) {
  if (!control) return fn();
  control.armed = true;
  try {
    return await fn();
  } finally {
    control.armed = false;
  }
}
async function runCliInteractiveOAuth(client, redirectUrlProvider, callbackUrlConfig, options) {
  const result = await withArmedAutoOpen(
    options?.autoOpenControl,
    () => runRunnerInteractiveOAuth({
      client,
      redirectUrlProvider,
      callbackListen: callbackUrlConfig,
      createCallbackServer: createOAuthCallbackServer,
      authorizationUrl: options?.authorizationUrl,
      authChallenge: options?.authChallenge
    })
  );
  if (result.kind === "insufficient_scope") {
    throw new Error(stepUpInsufficientScopeMessage(result.challenge));
  }
  if (result.kind === "success") {
    process.stderr.write("Authorization complete.\n");
  }
}
async function handleCliAuthRecoveryRequired(client, error, redirectUrlProvider, callbackUrlConfig, serverSettings, options) {
  const confirmStepUp = options?.confirmStepUp ?? (() => confirmStepUpFromStdin(options?.stepUpPromptTimeoutMs));
  if (isStandardOAuthStepUp2(error.authChallenge, serverSettings)) {
    if (await client.checkAuthChallengeSatisfied(error.authChallenge)) {
      return;
    }
    assertInteractiveOAuthAllowed(options);
    const proceed = await promptStepUpConfirm(
      error.authChallenge,
      confirmStepUp
    );
    if (!proceed) {
      throw new Error("Step-up authorization declined.");
    }
  } else if (await client.checkAuthChallengeSatisfied(error.authChallenge)) {
    return;
  } else {
    assertInteractiveOAuthAllowed(options);
  }
  await runCliInteractiveOAuth(client, redirectUrlProvider, callbackUrlConfig, {
    authorizationUrl: error.authorizationUrl,
    autoOpenControl: options?.autoOpenControl,
    ...error.authChallenge.reason === "insufficient_scope" && {
      authChallenge: error.authChallenge
    }
  });
}
async function connectInspectorWithOAuth(inspectorClient, serverConfig, redirectUrlProvider, callbackUrlConfig, serverSettings, options) {
  try {
    await inspectorClient.connect();
  } catch (err) {
    if (!isOAuthCapableServerConfig(serverConfig)) {
      throw err;
    }
    if (err instanceof AuthRecoveryRequiredError) {
      if (options?.storedAuthOnly) {
        if (await inspectorClient.checkAuthChallengeSatisfied(err.authChallenge)) {
          await inspectorClient.connect();
          return;
        }
        authRequiredFailure(
          err.message || "Authentication required and --stored-auth-only is set; refusing interactive OAuth."
        );
      }
      await handleCliAuthRecoveryRequired(
        inspectorClient,
        err,
        redirectUrlProvider,
        callbackUrlConfig,
        serverSettings,
        options
      );
      await inspectorClient.connect();
      return;
    }
    if (isUnauthorizedError(err)) {
      if (options?.storedAuthOnly) {
        authRequiredFailure(
          err instanceof Error ? err.message : "Authentication required and --stored-auth-only is set; refusing interactive OAuth."
        );
      }
      assertInteractiveOAuthAllowed(options);
      await inspectorClient.disconnect().catch(() => {
      });
      await runCliInteractiveOAuth(
        inspectorClient,
        redirectUrlProvider,
        callbackUrlConfig,
        { autoOpenControl: options?.autoOpenControl }
      );
      await inspectorClient.connect();
      return;
    }
    throw err;
  }
}
async function withCliAuthRecoveryRetry(inspectorClient, serverConfig, redirectUrlProvider, callbackUrlConfig, serverSettings, fn, options) {
  try {
    return await fn();
  } catch (err) {
    if (!isOAuthCapableServerConfig(serverConfig)) {
      throw err;
    }
    if (err instanceof AuthRecoveryRequiredError) {
      if (options?.storedAuthOnly) {
        if (await inspectorClient.checkAuthChallengeSatisfied(err.authChallenge)) {
          return await fn();
        }
        authRequiredFailure(
          err.message || "Authentication required and --stored-auth-only is set; refusing interactive OAuth."
        );
      }
      await handleCliAuthRecoveryRequired(
        inspectorClient,
        err,
        redirectUrlProvider,
        callbackUrlConfig,
        serverSettings,
        options
      );
      await inspectorClient.connect();
      process.stderr.write("Authorization complete. Retrying\u2026\n");
      return await fn();
    }
    if (isUnauthorizedError(err)) {
      if (options?.storedAuthOnly) {
        authRequiredFailure(
          err instanceof Error ? err.message : "Authentication required and --stored-auth-only is set; refusing interactive OAuth."
        );
      }
      assertInteractiveOAuthAllowed(options);
      await inspectorClient.disconnect().catch(() => {
      });
      await runCliInteractiveOAuth(
        inspectorClient,
        redirectUrlProvider,
        callbackUrlConfig,
        { autoOpenControl: options?.autoOpenControl }
      );
      await inspectorClient.connect();
      process.stderr.write("Authorization complete. Retrying\u2026\n");
      return await fn();
    }
    throw err;
  }
}

// src/cli.ts
import { LoggingLevelSchema as LoggingLevelSchema2 } from "@modelcontextprotocol/core";

// ../../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}`
  );
}

// src/cli.ts
var validLogLevels = Object.values(
  LoggingLevelSchema2.enum
);
var CLI_CLIENT_NAME = "inspector-cli";
async function callMethod(serverConfig, serverSettings, args, clientConfig, cliAuthOverrides, callbackUrlConfig, storedAuthOnly, relogin) {
  if (relogin) {
    if (!("url" in serverConfig && serverConfig.url)) {
      throw new Error(
        "--relogin requires an HTTP/SSE server URL (no OAuth store entry for stdio)"
      );
    }
    await clearStoredAuthForRelogin(serverConfig.url);
  }
  const clientIdentity = {
    name: CLI_CLIENT_NAME,
    version: readInspectorVersion(import.meta.url)
  };
  const environment = {
    transport: createTransportNode
  };
  const redirectUrlProvider = new MutableRedirectUrlProvider();
  const autoOpenControl = { armed: false };
  if (isOAuthCapableServerConfig(serverConfig)) {
    redirectUrlProvider.redirectUrl = formatRunnerOAuthRedirectUrl(callbackUrlConfig);
    environment.oauth = {
      storage: new NodeOAuthStorage(),
      navigation: createCliOAuthNavigation({
        autoOpenControl,
        disableAutoOpen: storedAuthOnly
      }),
      redirectUrlProvider
    };
  }
  const clientAuthOptions = buildRunnerClientAuthOptions(
    clientConfig,
    serverSettings,
    cliAuthOverrides
  );
  const inspectorClient = new InspectorClient(serverConfig, {
    environment,
    clientIdentity,
    initialLoggingLevel: "debug",
    progress: false,
    sample: false,
    elicit: false,
    // Advertise the roots configured for this server in mcp.json, exactly as
    // web does (`App.tsx`) so both answer `roots/list` with the same content.
    // Passing the option (even empty) is what negotiates `capabilities.roots`
    // at `initialize` and registers the `roots/list` handler. Omitting it meant
    // a server that asks for roots on its own — `server-filesystem` does, at
    // `initialize` — got -32601, and `--method roots/set` could not announce
    // the change at all: the SDK refuses `roots/list_changed` from a client
    // that never declared it, which `setRoots` logged as a send failure (#1797).
    roots: cleanRoots(serverSettings?.roots ?? []),
    serverSettings,
    // Per-server protocol era (SEP §7.8) from mcp.json → SDK versionNegotiation.
    // Absent era defaults to legacy in the InspectorClient constructor (#1626).
    ...serverSettings?.protocolEra && {
      versionNegotiation: eraToVersionNegotiation(serverSettings.protocolEra)
    },
    ...clientAuthOptions
  });
  try {
    await connectInspectorWithOAuth(
      inspectorClient,
      serverConfig,
      redirectUrlProvider,
      callbackUrlConfig,
      serverSettings,
      { storedAuthOnly, autoOpenControl }
    );
    const outcome = await withCliAuthRecoveryRetry(
      inspectorClient,
      serverConfig,
      redirectUrlProvider,
      callbackUrlConfig,
      serverSettings,
      () => runMethod(inspectorClient, args),
      { storedAuthOnly, autoOpenControl }
    );
    await consumeMethodOutcome(outcome, args);
  } finally {
    await inspectorClient.disconnect();
  }
}
function normalizeServerUrl2(serverUrl) {
  try {
    return new URL(serverUrl).href;
  } catch {
    return serverUrl;
  }
}
async function readOAuthSnapshot(statePath) {
  const { readFile: readFile2 } = await import("fs/promises");
  try {
    const text = await readFile2(statePath, "utf8");
    const snapshot = parseOAuthPersistBlob(text);
    if (snapshot) return snapshot;
  } catch {
  }
  return { servers: {}, idpSessions: {} };
}
async function readOAuthServers(statePath) {
  return (await readOAuthSnapshot(statePath)).servers;
}
function findStoredServerState(servers, serverUrl) {
  const normalized = normalizeServerUrl2(serverUrl);
  if (servers[normalized])
    return { key: normalized, state: servers[normalized] };
  if (servers[serverUrl]) return { key: serverUrl, state: servers[serverUrl] };
  return void 0;
}
function findStoredToken(servers, serverUrl) {
  return findStoredServerState(servers, serverUrl)?.state.tokens?.access_token;
}
async function refreshStoredAuthToken(serverUrl, statePath, deps = {}) {
  const refresh = deps.refresh ?? refreshAuthorization;
  const discover = deps.discover ?? discoverAuthorizationServerMetadata5;
  const snapshot = await readOAuthSnapshot(statePath);
  const servers = snapshot.servers;
  const found = findStoredServerState(servers, serverUrl);
  const refreshToken = found?.state.tokens?.refresh_token;
  const clientInformation = found?.state.clientInformation;
  if (!found || !refreshToken) {
    throw new CliExitCodeError(
      EXIT_CODES.AUTH_REQUIRED,
      `No stored refresh token for ${normalizeServerUrl2(serverUrl)} in ${statePath}. Complete the OAuth flow in the web inspector first.`,
      { code: "no_stored_token", url: serverUrl }
    );
  }
  if (!clientInformation) {
    throw new CliExitCodeError(
      EXIT_CODES.AUTH_REQUIRED,
      `Stored auth for ${normalizeServerUrl2(serverUrl)} has a refresh token but no client information; cannot refresh. Re-authorize in the web inspector.`,
      { code: "no_client_information", url: serverUrl }
    );
  }
  const authServerUrl = found.state.serverMetadata?.issuer ? new URL(found.state.serverMetadata.issuer) : getAuthorizationServerUrl(serverUrl);
  const metadata = found.state.serverMetadata ?? await discover(authServerUrl) ?? void 0;
  let tokens;
  try {
    tokens = await refresh(authServerUrl, {
      metadata,
      clientInformation,
      refreshToken,
      resource: new URL(serverUrl)
    });
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    throw new CliExitCodeError(
      EXIT_CODES.AUTH_REQUIRED,
      `Failed to refresh the stored OAuth token for ${normalizeServerUrl2(serverUrl)}: ${message}. Re-authorize in the web inspector.`,
      { code: "refresh_failed", url: serverUrl }
    );
  }
  servers[found.key] = { ...found.state, tokens };
  await writeStoreFile(statePath, serializeOAuthPersistBlob(snapshot));
  return tokens.access_token;
}
async function waitForStoredToken(serverUrl, statePath, timeoutSec) {
  const key = normalizeServerUrl2(serverUrl);
  const deadline = Date.now() + timeoutSec * 1e3;
  for (; ; ) {
    const servers = await readOAuthServers(statePath);
    const token = findStoredToken(servers, serverUrl);
    if (token) return token;
    if (Date.now() >= deadline) {
      const stored = Object.keys(servers);
      throw new CliExitCodeError(
        EXIT_CODES.AUTH_REQUIRED,
        `--wait-for-auth timed out after ${timeoutSec}s; no stored OAuth token for ${key} in ${statePath}.` + (stored.length > 0 ? ` Stored keys: ${stored.join(", ")}.` : " No tokens stored yet."),
        { code: "auth_wait_timeout", url: serverUrl }
      );
    }
    await new Promise((r2) => setTimeout(r2, 500));
  }
}
function deepLinkTransport(serverUrl, transport) {
  if (transport === "sse") return "sse";
  if (transport === "http") return "http";
  try {
    if (new URL(serverUrl).pathname.endsWith("/sse")) return "sse";
  } catch {
  }
  return "http";
}
function buildHandoff(serverUrl, statePath, transport) {
  const host = process.env.HOST || "127.0.0.1";
  const linkHost = isAllInterfacesHost(host) ? "localhost" : canonicalUrlHost(host);
  const clientPort = process.env.CLIENT_PORT || "6274";
  const sandboxPort = process.env.MCP_SANDBOX_PORT || "6275";
  const apiToken = process.env.MCP_INSPECTOR_API_TOKEN || void 0;
  const normalizedUrl = normalizeServerUrl2(serverUrl);
  const params = new URLSearchParams({
    serverUrl: normalizedUrl,
    transport: deepLinkTransport(serverUrl, transport)
  });
  if (apiToken) params.set("autoConnect", apiToken);
  return {
    serverUrl: normalizedUrl,
    deepLink: `http://${linkHost}:${clientPort}/?${params.toString()}`,
    portForwardCmd: `coder port-forward <workspace> --tcp ${clientPort}:${clientPort} --tcp ${sandboxPort}:${sandboxPort}`,
    oauthStatePath: statePath,
    apiToken: apiToken ?? null,
    note: apiToken === void 0 ? "MCP_INSPECTOR_API_TOKEN is not set; the deep-link autoConnect gate will reject \u2014 launch the web inspector with a known token first." : void 0
  };
}
function parseKeyValuePair2(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.`
    );
  }
  let parsedValue;
  try {
    parsedValue = JSON.parse(val);
  } catch {
    parsedValue = val;
  }
  return { ...previous, [key]: parsedValue };
}
async function parseArgs(argv) {
  const program = new Command();
  program.exitOverride((err) => {
    if (err.exitCode !== 0) throw err;
  });
  const rawArgs = argv ?? process.argv;
  const scriptArgs = rawArgs.slice(2);
  const dashDashIndex = scriptArgs.indexOf("--");
  let targetArgs = [];
  let optionArgs = [];
  if (dashDashIndex >= 0) {
    targetArgs = scriptArgs.slice(0, dashDashIndex);
    optionArgs = scriptArgs.slice(dashDashIndex + 1);
  } else {
    let i2 = 0;
    while (i2 < scriptArgs.length && !scriptArgs[i2].startsWith("-")) {
      targetArgs.push(scriptArgs[i2]);
      i2++;
    }
    optionArgs = scriptArgs.slice(i2);
  }
  const preArgs = [
    rawArgs[0] ?? "node",
    rawArgs[1] ?? "inspector-cli",
    ...optionArgs
  ];
  program.name("inspector-cli").allowUnknownOption().argument(
    "[target...]",
    "Command and arguments or URL of the MCP server (or use --config and --server)"
  ).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("--server <name>", "Server name from config/catalog file").option(
    "-e <env>",
    "Environment variables for the server (KEY=VALUE)",
    parseKeyValuePair,
    {}
  ).option("--method <method>", "Method to invoke").option("--tool-name <toolName>", "Tool name (for tools/call method)").option(
    "--tool-arg <pairs...>",
    "Tool argument as key=value pair",
    parseKeyValuePair2,
    {}
  ).option("--uri <uri>", "URI of the resource (for resources/read method)").option(
    "--prompt-name <promptName>",
    "Name of the prompt (for prompts/get method)"
  ).option(
    "--prompt-args <pairs...>",
    "Prompt arguments as key=value pairs",
    parseKeyValuePair2,
    {}
  ).option(
    "--log-level <level>",
    "Logging level (for logging/setLevel method)",
    (value) => {
      if (!validLogLevels.includes(value)) {
        throw new Error(
          `Invalid log level: ${value}. Valid levels are: ${validLogLevels.join(", ")}`
        );
      }
      return value;
    }
  ).option("--cwd <path>", "Working directory for stdio server process").option(
    "--transport <type>",
    "Transport type (sse, http, or stdio). Auto-detected from URL: /mcp \u2192 http, /sse \u2192 sse, commands \u2192 stdio",
    (value) => {
      const validTransports = ["sse", "http", "stdio"];
      if (!validTransports.includes(value)) {
        throw new Error(
          `Invalid transport type: ${value}. Valid types are: ${validTransports.join(", ")}`
        );
      }
      return value;
    }
  ).option("--server-url <url>", "Server URL for SSE/HTTP transport").option(
    "--header <headers...>",
    'HTTP headers as "HeaderName: Value" pairs (for HTTP/SSE transports)',
    parseHeaderPair,
    {}
  ).option(
    "--metadata <pairs...>",
    "General metadata as key=value pairs (applied to all methods)",
    parseKeyValuePair2,
    {}
  ).option(
    "--tool-metadata <pairs...>",
    "Tool-specific metadata as key=value pairs (for tools/call method only)",
    parseKeyValuePair2,
    {}
  ).option(
    "--app-info",
    "Probe the tool's MCP App UI metadata (resourceUri, csp, permissions, domain) and emit it as one JSON line; exit 2 when the tool has no app. Use with --method tools/call --tool-name <name> (the tool itself is not invoked) or --method tools/list (one NDJSON line per tool)."
  ).option(
    "--connect-timeout <ms>",
    `Connection timeout in ms (default ${DEFAULT_CONNECT_TIMEOUT_MS} for ad-hoc --server-url / target invocations; 0 = no timeout).`,
    (v2) => {
      const n = Number(v2);
      if (!Number.isFinite(n) || n < 0) {
        throw new Error(`--connect-timeout must be a non-negative number.`);
      }
      return n;
    }
  ).option(
    "--format <format>",
    "Output format: text (default; pretty-printed) or json (one JSON object on stdout, no banners).",
    (v2) => {
      if (v2 !== "text" && v2 !== "json") {
        throw new Error(`--format must be 'text' or 'json'.`);
      }
      return v2;
    }
  ).option(
    "--tool-args-json <json>",
    `Tool arguments as a single JSON object (e.g. '{"zip":"10001"}'). Values are passed verbatim \u2014 no key=value coercion. Mutually exclusive with --tool-arg.`
  ).option(
    "--client-config <path>",
    "Install-level client config (default: ~/.mcp-inspector/storage/client.json, or MCP_CLIENT_CONFIG_PATH)"
  ).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(
    "--callback-url <url>",
    `OAuth redirect/callback listener URL; must be loopback (default: ${DEFAULT_RUNNER_OAUTH_CALLBACK_URL}, or MCP_OAUTH_CALLBACK_URL)`
  ).option(
    "--use-stored-auth",
    "Read the OAuth access token for --server-url from the OAuth state file (written by the web inspector) and inject it as Authorization: Bearer."
  ).option(
    "--stored-auth-only",
    "Never start interactive OAuth; use the shared store if present, otherwise fail with auth_required. Preferred for CI/non-interactive runs. No-op when the server does not require auth."
  ).option(
    "--relogin",
    "Delete stored OAuth for this server URL from the shared store before connect (HTTP/SSE URL keys only); interactive login runs only if the server requires auth. Rejected for stdio (no URL-keyed store entry)"
  ).option(
    "--wait-for-auth <sec>",
    "Poll the OAuth state file until a token for --server-url appears (or the timeout elapses), then proceed as if --use-stored-auth were set. Use after handing off to a human to complete OAuth in a browser.",
    (v2) => {
      const n = Number(v2);
      if (!Number.isFinite(n) || n <= 0) {
        throw new Error(
          `--wait-for-auth must be a positive number of seconds.`
        );
      }
      return n;
    }
  ).option(
    "--list-stored-auth",
    "Print the server URLs that have a stored OAuth token (one JSON object on stdout) and exit. No server connection is made."
  ).option(
    "--print-handoff",
    "Print a JSON handoff block (deepLink, portForwardCmd, oauthStatePath, apiToken) for --server-url and exit. No server connection is made."
  );
  program.parse(preArgs);
  const options = program.opts();
  if (options.relogin) {
    if (options.storedAuthOnly) {
      throw new Error("--relogin cannot be combined with --stored-auth-only");
    }
    if (options.useStoredAuth || options.waitForAuth !== void 0) {
      throw new Error(
        "--relogin cannot be combined with --use-stored-auth or --wait-for-auth"
      );
    }
    if (options.listStoredAuth || options.printHandoff) {
      throw new Error(
        "--relogin cannot be combined with --list-stored-auth or --print-handoff"
      );
    }
    if (options.method === "servers/list" || options.method === "servers/show") {
      throw new Error(
        "--relogin cannot be combined with --method servers/list or servers/show (no OAuth connect)"
      );
    }
  }
  const oauthStatePath = getStateFilePath();
  if (options.listStoredAuth) {
    const servers = await readOAuthServers(oauthStatePath);
    const withToken = Object.entries(servers).filter(([, v2]) => Boolean(v2.tokens?.access_token)).map(([k2]) => k2);
    await awaitableLog(
      JSON.stringify({ oauthStatePath, storedServerUrls: withToken }) + "\n"
    );
    return { shortCircuit: true };
  }
  if (options.printHandoff) {
    if (!options.serverUrl) {
      throw new Error("--print-handoff requires --server-url");
    }
    await awaitableLog(
      JSON.stringify(
        buildHandoff(options.serverUrl, oauthStatePath, options.transport)
      ) + "\n"
    );
    return { shortCircuit: true };
  }
  if (!options.method) {
    throw new Error(
      "Method is required. Use --method to specify the method to invoke."
    );
  }
  const isCatalogMethod = options.method === "servers/list" || options.method === "servers/show";
  if (!isCatalogMethod && !isOneShotMethod(options.method)) {
    throw new Error(
      `Unsupported method: ${options.method}. Supported --cli methods: ${ONE_SHOT_METHODS.join(", ")}, servers/list, servers/show.`
    );
  }
  const adHoc = targetArgs.length > 0 || Boolean(options.transport) || Boolean(options.serverUrl?.trim());
  const envCatalog = adHoc ? void 0 : process.env.MCP_CATALOG_PATH;
  const serverOptions = {
    // `?.trim() ||` (not `??`) so an explicit empty `--catalog ""` still falls
    // back to MCP_CATALOG_PATH — keeps CLI and TUI flag resolution identical.
    catalogPath: options.catalog?.trim() || envCatalog,
    configPath: options.config?.trim() || void 0,
    target: targetArgs.length > 0 ? targetArgs : void 0,
    transport: options.transport,
    serverUrl: options.serverUrl,
    cwd: options.cwd,
    env: options.e,
    // `--header` is merged into the resolved server's settings (overriding any
    // file-level headers); file timeouts/OAuth are preserved. See #1482.
    headers: options.header
  };
  if (options.method === "servers/list") {
    const servers = await listServerEntries(serverOptions);
    await writeFormattedResult(
      { servers },
      options.format === "json" ? "json" : "text"
    );
    return { shortCircuit: true };
  }
  if (options.method === "servers/show") {
    if (!options.server?.trim()) {
      throw new Error(
        "servers/show requires --server <name> to select a catalog entry."
      );
    }
    const server = await showServerEntry(options.server, serverOptions);
    await writeFormattedResult(
      server,
      options.format === "json" ? "json" : "text"
    );
    return { shortCircuit: true };
  }
  if (options.waitForAuth !== void 0 || options.useStoredAuth) {
    if (!options.serverUrl) {
      throw new Error(
        `${options.waitForAuth !== void 0 ? "--wait-for-auth" : "--use-stored-auth"} requires --server-url`
      );
    }
    let token;
    if (options.waitForAuth !== void 0) {
      token = await waitForStoredToken(
        options.serverUrl,
        oauthStatePath,
        options.waitForAuth
      );
    } else {
      const servers = await readOAuthServers(oauthStatePath);
      const stored = findStoredServerState(servers, options.serverUrl);
      if (stored?.state.tokens?.refresh_token) {
        const storedAccess = stored.state.tokens.access_token;
        try {
          token = await refreshStoredAuthToken(
            options.serverUrl,
            oauthStatePath
          );
        } catch (err) {
          if (!storedAccess) throw err;
          token = storedAccess;
        }
      } else {
        const found = findStoredToken(servers, options.serverUrl);
        if (!found) {
          const key = normalizeServerUrl2(options.serverUrl);
          const storedKeys = Object.keys(servers);
          throw new CliExitCodeError(
            EXIT_CODES.AUTH_REQUIRED,
            `No stored OAuth token for ${key} in ${oauthStatePath}. Complete the OAuth flow in the web inspector first.` + (storedKeys.length > 0 ? ` Stored keys: ${storedKeys.join(", ")}.` : ""),
            { code: "no_stored_token", url: options.serverUrl }
          );
        }
        token = found;
      }
    }
    serverOptions.headers = {
      ...serverOptions.headers ?? {},
      Authorization: `Bearer ${token}`
    };
  }
  const entries = await loadServerEntries(serverOptions);
  const selected = selectServerEntry(entries, options.server);
  const serverConfig = selected.config;
  const serverSettings = withConnectTimeout(
    selected.settings,
    options.connectTimeout ?? (adHoc ? DEFAULT_CONNECT_TIMEOUT_MS : void 0)
  );
  if (options.appInfo && options.method !== "tools/call" && options.method !== "tools/list") {
    throw new Error(
      "--app-info requires --method tools/call (with --tool-name) or --method tools/list."
    );
  }
  let toolArg = options.toolArg;
  if (options.toolArgsJson !== void 0) {
    if (toolArg && Object.keys(toolArg).length > 0) {
      throw new Error(
        "--tool-args-json cannot be combined with --tool-arg; pick one."
      );
    }
    let parsed;
    try {
      parsed = JSON.parse(options.toolArgsJson);
    } catch (e2) {
      throw new Error(
        `--tool-args-json is not valid JSON: ${e2.message}`
      );
    }
    if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
      throw new Error("--tool-args-json must be a JSON object.");
    }
    toolArg = parsed;
  }
  const methodArgs = {
    method: options.method,
    toolName: options.toolName,
    toolArg,
    uri: options.uri,
    promptName: options.promptName,
    promptArgs: options.promptArgs,
    logLevel: options.logLevel,
    metadata: options.metadata ? Object.fromEntries(
      Object.entries(options.metadata).map(([key, value]) => [
        key,
        metaValueToString(value)
      ])
    ) : void 0,
    toolMeta: options.toolMetadata ? Object.fromEntries(
      Object.entries(options.toolMetadata).map(([key, value]) => [
        key,
        metaValueToString(value)
      ])
    ) : void 0,
    appInfo: options.appInfo === true,
    format: options.format
  };
  return {
    serverConfig,
    serverSettings,
    methodArgs,
    clientConfigPath: options.clientConfig,
    clientId: options.clientId,
    clientSecret: options.clientSecret,
    clientMetadataUrl: options.clientMetadataUrl,
    callbackUrl: options.callbackUrl,
    storedAuthOnly: options.storedAuthOnly === true,
    relogin: options.relogin === true
  };
}
async function runCli(argv) {
  const parsed = await parseArgs(argv ?? process.argv);
  if (parsed.shortCircuit) return;
  const {
    serverConfig,
    serverSettings,
    methodArgs,
    clientConfigPath,
    clientId,
    clientSecret,
    clientMetadataUrl,
    callbackUrl,
    storedAuthOnly,
    relogin
  } = parsed;
  const clientConfig = await loadRunnerClientConfig({ clientConfigPath });
  let callbackUrlConfig;
  try {
    callbackUrlConfig = parseRunnerOAuthCallbackUrl(callbackUrl);
  } catch (err) {
    throw new CliExitCodeError(
      EXIT_CODES.USAGE,
      err instanceof Error ? err.message : String(err)
    );
  }
  await callMethod(
    serverConfig,
    serverSettings,
    methodArgs,
    clientConfig,
    {
      clientId,
      clientSecret,
      clientMetadataUrl
    },
    callbackUrlConfig,
    storedAuthOnly === true,
    relogin === true
  );
}

// src/index.ts
var __filename = fileURLToPath2(import.meta.url);
var isMain = process.argv[1] !== void 0 && resolve3(process.argv[1]) === resolve3(__filename);
if (isMain) {
  runCli(process.argv).then(() => process.exit(0)).catch(handleError);
}
export {
  handleError,
  runCli,
  validLogLevels
};
