import { Client, StreamableHTTPClientTransport, SSEClientTransport, discoverOAuthProtectedResourceMetadata } from '@modelcontextprotocol/client';

// src/redaction.ts
function redactSensitiveValue(value) {
  return redactSensitiveValueAtPath(value, []);
}
function redactSensitiveValueAtPath(value, path) {
  if (Array.isArray(value)) {
    return value.map((entry) => redactSensitiveValueAtPath(entry, path));
  }
  if (!value || typeof value !== "object") {
    return typeof value === "string" ? redactSensitiveString(value) : value;
  }
  return Object.fromEntries(
    Object.entries(value).map(([key, entryValue]) => [
      key,
      shouldRedactKey(key, entryValue, path) ? "[REDACTED]" : redactSensitiveValueAtPath(entryValue, [...path, key])
    ])
  );
}
function redactSensitiveString(value) {
  return value.replace(
    /\b(authorization|proxy-authorization|cookie|set-cookie)\s*:\s*[^\r\n]*/giu,
    (_match, headerName) => `${headerName}: [REDACTED]`
  ).replace(
    /\bBearer\s+(?![A-Za-z_][A-Za-z0-9_-]*=)([A-Za-z0-9\-._~+/]+=*)/giu,
    "Bearer [REDACTED]"
  ).replace(
    /\b(access_token|refresh_token|client_secret|id_token|code|code_verifier|accessToken|refreshToken|clientSecret|idToken|codeVerifier)=([^&\s]+)/giu,
    "$1=[REDACTED]"
  ).replace(
    /(["']?(?:access_token|refresh_token|client_secret|id_token|code|code_verifier|accessToken|refreshToken|clientSecret|idToken|codeVerifier)["']?\s*:\s*["'])[^"']*(["'])/giu,
    "$1[REDACTED]$2"
  );
}
function shouldRedactKey(key, value, path) {
  const normalized = key.toLowerCase().replace(/[^a-z0-9]/gu, "");
  if (normalized === "code") {
    return shouldRedactAuthorizationCodeValue(value, path);
  }
  if (normalized === "authorization" || normalized === "proxyauthorization" || normalized === "cookie" || normalized === "setcookie") {
    return true;
  }
  return (normalized === "codeverifier" || normalized === "accesstoken" || normalized.endsWith("accesstoken") || normalized === "refreshtoken" || normalized.endsWith("refreshtoken") || normalized === "clientsecret" || normalized.endsWith("clientsecret") || normalized === "idtoken" || normalized.endsWith("idtoken")) && shouldRedactSecretValue(value) || normalized === "apikey" || normalized === "xapikey";
}
function shouldRedactSecretValue(value) {
  return typeof value === "string" && value.length > 0;
}
function shouldRedactAuthorizationCodeValue(value, path) {
  if (path[path.length - 1] === "error" || path[path.length - 1] === "snapshotError") {
    return false;
  }
  if (typeof value !== "string") {
    return false;
  }
  if (/^[A-Z0-9_:-]+$/u.test(value)) {
    return false;
  }
  return value.length > 0;
}

// src/oauth/state-machines/shared/urls.ts
function buildResourceMetadataUrl(serverUrl) {
  const url = new URL(serverUrl);
  if (url.pathname !== "/" && url.pathname !== "") {
    const pathname = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
    return new URL(
      `/.well-known/oauth-protected-resource${pathname}`,
      url.origin
    ).toString();
  }
  return new URL(
    "/.well-known/oauth-protected-resource",
    url.origin
  ).toString();
}

// src/oauth/authorization-plan.ts
function resolveRegistrationStrategies(protocolVersion, authServerMetadata) {
  const strategies = [
    "preregistered"
  ];
  if (authServerMetadata?.registration_endpoint) {
    strategies.push("dcr");
  }
  if (protocolVersion === "2025-11-25" && authServerMetadata?.client_id_metadata_document_supported === true) {
    strategies.push("cimd");
  }
  return strategies;
}

// src/mcp-client-manager/error-utils.ts
function isMethodUnavailableError(error, method) {
  if (!(error instanceof Error)) {
    return false;
  }
  const message = error.message.toLowerCase();
  const methodTokens = /* @__PURE__ */ new Set();
  methodTokens.add(method.toLowerCase());
  for (const part of method.split(/[/:._-]/)) {
    if (part) {
      methodTokens.add(part.toLowerCase());
    }
  }
  const indicators = [
    "method not found",
    "not implemented",
    "unsupported",
    "does not support",
    "unimplemented",
    "unknown method",
    "unknown mcp method"
  ];
  const indicatorMatch = indicators.some(
    (indicator) => message.includes(indicator)
  );
  if (!indicatorMatch) {
    return false;
  }
  if (Array.from(methodTokens).some((token) => message.includes(token))) {
    return true;
  }
  return true;
}

// src/mcp-client-manager/errors.ts
function hasNumericCode(error) {
  return error instanceof Error && "code" in error && typeof error.code === "number";
}
function isAuthError(error) {
  if (!(error instanceof Error)) {
    return { isAuth: false };
  }
  if (error.name === "UnauthorizedError") {
    return { isAuth: true, statusCode: 401 };
  }
  if (error.name === "MCPAuthError") {
    const statusCode = "statusCode" in error && typeof error.statusCode === "number" ? error.statusCode : void 0;
    return { isAuth: true, statusCode };
  }
  if (hasNumericCode(error)) {
    const code = error.code;
    if (code === 401 || code === 403) {
      return { isAuth: true, statusCode: code };
    }
  }
  const message = error.message.toLowerCase();
  const authPatterns = [
    "unauthorized",
    "invalid_token",
    "invalid token",
    "token expired",
    "token has expired",
    "access denied",
    "authentication failed",
    "authentication required",
    "not authenticated",
    "forbidden"
  ];
  if (authPatterns.some((pattern) => message.includes(pattern))) {
    return { isAuth: true };
  }
  const statusMatch = message.match(/\b(status[:\s]*)?401\b|\bhttp\s*401\b/i);
  if (statusMatch) {
    return { isAuth: true, statusCode: 401 };
  }
  const forbiddenMatch = message.match(
    /\b(status[:\s]*)?403\b|\bhttp\s*403\b/i
  );
  if (forbiddenMatch) {
    return { isAuth: true, statusCode: 403 };
  }
  return { isAuth: false };
}

// src/retry.ts
var DEFAULT_RETRY_POLICY = {
  retries: 0,
  retryDelayMs: 3e3
};
var RETRYABLE_NODE_ERROR_CODES = /* @__PURE__ */ new Set([
  "ECONNREFUSED",
  "ECONNRESET",
  "EAI_AGAIN",
  "ENETDOWN",
  "ENETUNREACH",
  "ENOTFOUND",
  "EPIPE",
  "ETIMEDOUT",
  "UND_ERR_CONNECT_TIMEOUT",
  "UND_ERR_HEADERS_TIMEOUT",
  "UND_ERR_SOCKET"
]);
function toAbortError(reason) {
  if (reason instanceof Error) {
    return reason;
  }
  const error = new Error(
    reason == null ? "The operation was aborted." : String(reason)
  );
  error.name = "AbortError";
  return error;
}
function throwIfAborted(signal) {
  if (signal?.aborted) {
    throw toAbortError(signal.reason);
  }
}
function delay(ms, signal) {
  throwIfAborted(signal);
  if (ms <= 0) {
    return Promise.resolve();
  }
  return new Promise((resolve, reject) => {
    const timeoutId = setTimeout(() => {
      cleanup();
      resolve();
    }, ms);
    const onAbort = () => {
      clearTimeout(timeoutId);
      cleanup();
      reject(toAbortError(signal?.reason));
    };
    const cleanup = () => {
      signal?.removeEventListener("abort", onAbort);
    };
    signal?.addEventListener("abort", onAbort, { once: true });
  });
}
function extractHttpStatusCode(error) {
  if (!error || typeof error !== "object") {
    return void 0;
  }
  const statusCode = "statusCode" in error && typeof error.statusCode === "number" ? error.statusCode : void 0;
  if (statusCode !== void 0) {
    return statusCode;
  }
  const numericCode = "code" in error && typeof error.code === "number" ? error.code : void 0;
  if (numericCode !== void 0) {
    return numericCode;
  }
  if (!(error instanceof Error)) {
    return void 0;
  }
  const match = error.message.match(/\b(?:http|status)[:\s]+(\d{3})\b/i);
  if (!match) {
    return void 0;
  }
  const parsed = Number(match[1]);
  return Number.isFinite(parsed) ? parsed : void 0;
}
function extractNodeErrorCode(error) {
  if (!error || typeof error !== "object") {
    return void 0;
  }
  return "code" in error && typeof error.code === "string" ? error.code : void 0;
}
function normalizeRetryPolicy(policy) {
  return {
    retries: Math.max(0, policy?.retries ?? DEFAULT_RETRY_POLICY.retries),
    retryDelayMs: Math.max(
      0,
      policy?.retryDelayMs ?? DEFAULT_RETRY_POLICY.retryDelayMs
    )
  };
}
function isRetryableTransientError(error) {
  if (isAuthError(error).isAuth) {
    return false;
  }
  if (isMethodUnavailableError(error, "rpc")) {
    return false;
  }
  const statusCode = extractHttpStatusCode(error);
  if (statusCode === 408 || statusCode === 425 || statusCode === 429) {
    return true;
  }
  if (statusCode === 501) {
    return false;
  }
  if (statusCode !== void 0 && statusCode >= 500 && statusCode <= 599) {
    return true;
  }
  const nodeCode = extractNodeErrorCode(error)?.toUpperCase();
  if (nodeCode && RETRYABLE_NODE_ERROR_CODES.has(nodeCode)) {
    return true;
  }
  if (!(error instanceof Error)) {
    return false;
  }
  const message = error.message.toLowerCase();
  if (error.name === "AbortError" || message.includes("aborted")) {
    return false;
  }
  return [
    "connection reset",
    "connection refused",
    "connection terminated",
    "connect timeout",
    "dns lookup",
    "econn",
    "eai_again",
    "enotfound",
    "etimedout",
    "fetch failed",
    "network error",
    "network request failed",
    "socket hang up",
    "timed out",
    "timeout",
    "temporarily unavailable"
  ].some((pattern) => message.includes(pattern));
}
async function retryWithPolicy({
  policy,
  signal,
  operation,
  shouldRetryError,
  shouldRetryResult,
  onRetry
}) {
  const normalized = normalizeRetryPolicy(policy);
  for (let attempt = 0; ; attempt += 1) {
    throwIfAborted(signal);
    try {
      const result = await operation(attempt);
      const shouldRetry = attempt < normalized.retries && (shouldRetryResult?.(result, attempt) ?? false);
      if (!shouldRetry) {
        return result;
      }
      await onRetry?.({ attempt, result });
      await delay(normalized.retryDelayMs, signal);
    } catch (error) {
      const shouldRetry = attempt < normalized.retries && (shouldRetryError?.(error, attempt) ?? false);
      if (!shouldRetry) {
        throw error;
      }
      await onRetry?.({ attempt, error });
      await delay(normalized.retryDelayMs, signal);
    }
  }
}

// src/server-probe.ts
function normalizeProtocolVersion(value) {
  return value ?? "2025-11-25";
}
function normalizeHeaders(headers) {
  if (!headers) {
    return {};
  }
  const normalized = {};
  new Headers(headers).forEach((value, key) => {
    normalized[key] = value;
  });
  return normalized;
}
function lowerCaseHeaders(headers) {
  return Object.fromEntries(
    Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value])
  );
}
function removeAuthorizationHeader(headers) {
  return Object.fromEntries(
    Object.entries(headers).filter(
      ([key]) => key.toLowerCase() !== "authorization"
    )
  );
}
function initializeProtocolVersion(protocolVersion) {
  switch (protocolVersion) {
    case "2025-03-26":
    case "2025-06-18":
      return "2024-11-05";
    case "2025-11-25":
      return "2025-11-25";
    default: {
      const exhaustive = protocolVersion;
      return exhaustive;
    }
  }
}
function buildAuthServerMetadataUrls(protocolVersion, authServerUrl) {
  const url = new URL(authServerUrl);
  const urls = [];
  if (protocolVersion === "2025-11-25") {
    if (url.pathname === "/" || url.pathname === "") {
      urls.push(
        new URL(
          "/.well-known/oauth-authorization-server",
          url.origin
        ).toString()
      );
      urls.push(
        new URL("/.well-known/openid-configuration", url.origin).toString()
      );
      return urls;
    }
    const pathname2 = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
    urls.push(
      new URL(
        `/.well-known/oauth-authorization-server${pathname2}`,
        url.origin
      ).toString()
    );
    urls.push(
      new URL(
        `/.well-known/openid-configuration${pathname2}`,
        url.origin
      ).toString()
    );
    urls.push(
      new URL(
        `${pathname2}/.well-known/openid-configuration`,
        url.origin
      ).toString()
    );
    return urls;
  }
  if (url.pathname === "/" || url.pathname === "") {
    urls.push(
      new URL("/.well-known/oauth-authorization-server", url.origin).toString()
    );
    return urls;
  }
  const pathname = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
  urls.push(
    new URL(
      `/.well-known/oauth-authorization-server${pathname}`,
      url.origin
    ).toString()
  );
  urls.push(
    new URL("/.well-known/oauth-authorization-server", url.origin).toString()
  );
  return urls;
}
function parseJsonBody(text) {
  const trimmed = text.trim();
  if (!trimmed) {
    return void 0;
  }
  try {
    return JSON.parse(trimmed);
  } catch {
    return text;
  }
}
async function readResponseBody(response) {
  const contentType = response.headers.get("content-type") ?? void 0;
  if (contentType?.includes("text/event-stream")) {
    return { contentType };
  }
  const text = await response.text();
  if (!text) {
    return { body: void 0, contentType };
  }
  if (contentType?.includes("application/json") || contentType?.includes("+json")) {
    return {
      body: parseJsonBody(text),
      contentType
    };
  }
  return {
    body: parseJsonBody(text),
    contentType
  };
}
function withTimeoutSignal(timeoutMs) {
  if (!timeoutMs) {
    return {
      signal: void 0,
      cleanup: () => void 0,
      didTimeout: () => false
    };
  }
  const controller = new AbortController();
  let timedOut = false;
  const handle = setTimeout(() => {
    timedOut = true;
    controller.abort();
  }, timeoutMs);
  return {
    signal: controller.signal,
    cleanup: () => clearTimeout(handle),
    didTimeout: () => timedOut
  };
}
function createTimeoutError(timeoutMs) {
  const error = new Error(
    timeoutMs ? `Request timed out after ${timeoutMs}ms` : "Request timed out"
  );
  error.name = "TimeoutError";
  error.code = "ETIMEDOUT";
  return error;
}
function isRetryableProbeStatus(status) {
  return status === 408 || status === 425 || status === 429 || status >= 500 && status !== 501;
}
async function performRequest(fetchFn, attempt, timeoutMs) {
  const startedAt = Date.now();
  const { signal, cleanup, didTimeout } = withTimeoutSignal(timeoutMs);
  try {
    const response = await fetchFn(attempt.request.url, {
      method: attempt.request.method,
      headers: attempt.request.headers,
      body: attempt.request.body === void 0 ? void 0 : JSON.stringify(attempt.request.body),
      redirect: "follow",
      signal
    });
    const parsedBody = await readResponseBody(response);
    const normalizedHeaders = normalizeHeaders(response.headers);
    attempt.durationMs = Date.now() - startedAt;
    attempt.response = {
      status: response.status,
      statusText: response.statusText,
      headers: normalizedHeaders,
      body: parsedBody.body,
      contentType: parsedBody.contentType
    };
    return {
      status: response.status,
      statusText: response.statusText,
      headers: lowerCaseHeaders(normalizedHeaders),
      body: parsedBody.body,
      contentType: parsedBody.contentType
    };
  } catch (error) {
    const requestError = didTimeout() ? createTimeoutError(timeoutMs) : error;
    attempt.durationMs = Date.now() - startedAt;
    attempt.error = requestError instanceof Error ? requestError.message : String(requestError);
    throw requestError;
  } finally {
    cleanup();
  }
}
function extractInitializeInfo(body, contentType) {
  if (body && typeof body === "object" && "result" in body && body.result?.protocolVersion) {
    const result = body.result;
    return {
      protocolVersion: result.protocolVersion,
      serverInfo: result.serverInfo,
      capabilities: result.capabilities,
      contentType
    };
  }
  if (contentType?.includes("text/event-stream")) {
    return {
      contentType
    };
  }
  return void 0;
}
function buildInitializeRequest(config) {
  const protocolVersion = normalizeProtocolVersion(config.protocolVersion);
  const accessToken = config.accessToken?.trim();
  const headers = {
    ...normalizeHeaders(config.headers),
    "Content-Type": "application/json",
    Accept: "application/json, text/event-stream"
  };
  if (accessToken) {
    headers.Authorization = `Bearer ${accessToken}`;
  }
  return {
    name: "streamable_initialize",
    request: {
      method: "POST",
      url: config.url,
      headers,
      body: {
        jsonrpc: "2.0",
        id: 1,
        method: "initialize",
        params: {
          protocolVersion: initializeProtocolVersion(protocolVersion),
          capabilities: config.clientCapabilities ?? {},
          clientInfo: {
            name: config.clientName ?? "mcpjam-probe",
            version: config.clientVersion ?? "1.0.0"
          }
        }
      }
    },
    durationMs: 0
  };
}
function buildSseProbeRequest(config) {
  const accessToken = config.accessToken?.trim();
  const headers = {
    ...normalizeHeaders(config.headers),
    Accept: "text/event-stream"
  };
  if (accessToken) {
    headers.Authorization = `Bearer ${accessToken}`;
  }
  return {
    name: "sse_probe",
    request: {
      method: "GET",
      url: config.url,
      headers
    },
    durationMs: 0
  };
}
async function discoverOAuthDetails(config, attempts, optional, wwwAuthenticateHeader) {
  const protocolVersion = normalizeProtocolVersion(config.protocolVersion);
  const metadataHeaders = removeAuthorizationHeader(
    normalizeHeaders(config.headers)
  );
  const resourceMetadataUrlFromHeader = wwwAuthenticateHeader?.match(
    /resource_metadata="([^"]+)"/
  )?.[1];
  const resourceMetadataUrl = resourceMetadataUrlFromHeader ?? buildResourceMetadataUrl(config.url);
  const resourceMetadataAttempt = {
    name: "resource_metadata",
    request: {
      method: "GET",
      url: resourceMetadataUrl,
      headers: metadataHeaders
    },
    durationMs: 0
  };
  attempts.push(resourceMetadataAttempt);
  try {
    const loggingFetch = async (input, init = {}) => {
      const url = typeof input === "string" ? input : input.toString();
      const mergedHeaders = {
        ...metadataHeaders,
        ...normalizeHeaders(init.headers)
      };
      const attempt = resourceMetadataAttempt.request.url === url ? resourceMetadataAttempt : {
        name: "resource_metadata",
        request: {
          method: init.method ?? "GET",
          url,
          headers: mergedHeaders
        },
        durationMs: 0
      };
      if (attempt !== resourceMetadataAttempt) {
        attempts.push(attempt);
      } else {
        resourceMetadataAttempt.request.headers = mergedHeaders;
        resourceMetadataAttempt.request.method = init.method ?? "GET";
      }
      const response = await performRequest(
        config.fetchFn ?? fetch,
        attempt,
        config.timeoutMs
      );
      return new Response(
        response.body === void 0 ? null : JSON.stringify(response.body),
        {
          status: response.status,
          statusText: response.statusText,
          headers: response.headers
        }
      );
    };
    const metadata = await discoverOAuthProtectedResourceMetadata(
      config.url,
      resourceMetadataUrlFromHeader ? { resourceMetadataUrl: resourceMetadataUrlFromHeader } : void 0,
      loggingFetch
    );
    const authorizationServerUrl = metadata.authorization_servers?.[0] ?? config.url;
    const authMetadataUrls = buildAuthServerMetadataUrls(
      protocolVersion,
      authorizationServerUrl
    );
    let authorizationServerMetadata;
    let authorizationServerMetadataUrl;
    let lastAuthError;
    for (const authMetadataUrl of authMetadataUrls) {
      const authAttempt = {
        name: "authorization_server_metadata",
        request: {
          method: "GET",
          url: authMetadataUrl,
          headers: metadataHeaders
        },
        durationMs: 0
      };
      attempts.push(authAttempt);
      try {
        const response = await performRequest(
          config.fetchFn ?? fetch,
          authAttempt,
          config.timeoutMs
        );
        if (response.status >= 200 && response.status < 300 && response.body && typeof response.body === "object") {
          authorizationServerMetadata = response.body;
          authorizationServerMetadataUrl = authMetadataUrl;
          lastAuthError = void 0;
          break;
        }
        lastAuthError = `HTTP ${response.status} ${response.statusText}`;
      } catch (error) {
        lastAuthError = error instanceof Error ? error.message : String(error);
      }
    }
    return {
      required: !optional,
      optional,
      wwwAuthenticate: wwwAuthenticateHeader,
      resourceMetadataUrl,
      resourceMetadata: metadata,
      authorizationServerMetadataUrl,
      authorizationServerMetadata,
      registrationStrategies: resolveRegistrationStrategies(
        protocolVersion,
        authorizationServerMetadata
      ),
      ...lastAuthError ? { discoveryError: lastAuthError } : {}
    };
  } catch (error) {
    if (optional) {
      return {
        required: false,
        optional: false,
        resourceMetadataUrl,
        registrationStrategies: []
      };
    }
    return {
      required: true,
      optional: false,
      wwwAuthenticate: wwwAuthenticateHeader,
      resourceMetadataUrl,
      registrationStrategies: ["preregistered"],
      discoveryError: error instanceof Error ? error.message : String(error)
    };
  }
}
function baseOAuthResult() {
  return {
    required: false,
    optional: false,
    registrationStrategies: []
  };
}
function createProbeErrorResult(config, protocolVersion, attempts, error, retryable) {
  return {
    result: {
      url: config.url,
      protocolVersion,
      status: "error",
      transport: {
        attempts
      },
      oauth: baseOAuthResult(),
      error
    },
    retryable
  };
}
async function probeMcpServerOnce(config, attempts) {
  const protocolVersion = normalizeProtocolVersion(config.protocolVersion);
  const initializeAttempt = buildInitializeRequest(config);
  attempts.push(initializeAttempt);
  try {
    const initializeResponse = await performRequest(
      config.fetchFn ?? fetch,
      initializeAttempt,
      config.timeoutMs
    );
    const initializeInfo = extractInitializeInfo(
      initializeResponse.body,
      initializeResponse.contentType
    );
    const wwwAuthenticate = initializeResponse.headers["www-authenticate"];
    if (initializeResponse.status === 401) {
      return {
        result: {
          url: config.url,
          protocolVersion,
          status: "oauth_required",
          transport: {
            attempts
          },
          oauth: await discoverOAuthDetails(
            config,
            attempts,
            false,
            wwwAuthenticate
          )
        },
        retryable: false
      };
    }
    if (initializeResponse.status >= 200 && initializeResponse.status < 300) {
      const oauth = config.accessToken ? baseOAuthResult() : await discoverOAuthDetails(config, attempts, true, void 0).catch(
        () => baseOAuthResult()
      );
      if (initializeInfo) {
        return {
          result: {
            url: config.url,
            protocolVersion,
            status: "ready",
            transport: {
              selected: "streamable-http",
              attempts
            },
            initialize: initializeInfo,
            oauth
          },
          retryable: false
        };
      }
      return {
        result: {
          url: config.url,
          protocolVersion,
          status: "reachable",
          transport: {
            attempts
          },
          oauth,
          error: "Server responded to initialize but did not return a recognizable MCP initialize result."
        },
        retryable: false
      };
    }
    const shouldTrySse = initializeResponse.status === 404 || initializeResponse.status === 405 || initializeResponse.status === 406 || initializeResponse.status === 415 || initializeResponse.status === 501;
    if (!shouldTrySse && isRetryableProbeStatus(initializeResponse.status)) {
      return createProbeErrorResult(
        config,
        protocolVersion,
        attempts,
        `Server responded with HTTP ${initializeResponse.status} ${initializeResponse.statusText} to the initialize probe.`,
        true
      );
    }
    if (shouldTrySse) {
      const sseAttempt = buildSseProbeRequest(config);
      attempts.push(sseAttempt);
      const sseResponse = await performRequest(
        config.fetchFn ?? fetch,
        sseAttempt,
        config.timeoutMs
      );
      const wwwAuthenticateSse = sseResponse.headers["www-authenticate"];
      if (sseResponse.status === 401) {
        return {
          result: {
            url: config.url,
            protocolVersion,
            status: "oauth_required",
            transport: {
              attempts
            },
            oauth: await discoverOAuthDetails(
              config,
              attempts,
              false,
              wwwAuthenticateSse
            )
          },
          retryable: false
        };
      }
      if (sseResponse.status >= 200 && sseResponse.status < 300 && sseResponse.contentType?.includes("text/event-stream")) {
        const oauth = config.accessToken ? baseOAuthResult() : await discoverOAuthDetails(config, attempts, true, void 0).catch(
          () => baseOAuthResult()
        );
        return {
          result: {
            url: config.url,
            protocolVersion,
            status: "ready",
            transport: {
              selected: "sse",
              attempts
            },
            initialize: {
              contentType: sseResponse.contentType
            },
            oauth
          },
          retryable: false
        };
      }
      if (isRetryableProbeStatus(sseResponse.status)) {
        return createProbeErrorResult(
          config,
          protocolVersion,
          attempts,
          `Server responded with HTTP ${sseResponse.status} ${sseResponse.statusText} to the SSE probe.`,
          true
        );
      }
    }
    return {
      result: {
        url: config.url,
        protocolVersion,
        status: "reachable",
        transport: {
          attempts
        },
        oauth: baseOAuthResult(),
        error: `Server responded with HTTP ${initializeResponse.status} ${initializeResponse.statusText} to the initialize probe.`
      },
      retryable: false
    };
  } catch (error) {
    return createProbeErrorResult(
      config,
      protocolVersion,
      attempts,
      error instanceof Error ? error.message : String(error),
      isRetryableTransientError(error)
    );
  }
}
async function probeMcpServer(config) {
  const attempts = [];
  const outcome = await retryWithPolicy({
    policy: config.retryPolicy,
    operation: () => probeMcpServerOnce(config, attempts),
    shouldRetryResult: (result) => result.retryable
  });
  return outcome.result;
}

// src/mcp-client-manager/capabilities.ts
var MCP_UI_EXTENSION_ID = "io.modelcontextprotocol/ui";
var MCP_UI_RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
function isPlainObject(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function mergeExtensionsMaps(base, override) {
  if (!base && !override) {
    return void 0;
  }
  if (!base) {
    return { ...override };
  }
  if (!override) {
    return { ...base };
  }
  const merged = { ...base };
  for (const key of Object.keys(override)) {
    const b = merged[key];
    const o = override[key];
    if (isPlainObject(b) && isPlainObject(o)) {
      merged[key] = { ...b, ...o };
    } else {
      merged[key] = o;
    }
  }
  return merged;
}
function getDefaultClientCapabilities() {
  return {
    extensions: {
      [MCP_UI_EXTENSION_ID]: {
        mimeTypes: [MCP_UI_RESOURCE_MIME_TYPE]
      }
    }
  };
}
function normalizeClientCapabilities(capabilities) {
  return {
    ...capabilities ?? {}
  };
}
function mergeClientCapabilities(base, overrides) {
  const baseRecord = base;
  const overrideRecord = overrides;
  const merged = {
    ...base ?? {},
    ...overrides ?? {}
  };
  if (overrideRecord && Object.prototype.hasOwnProperty.call(overrideRecord, "extensions")) {
    const overExt = overrideRecord.extensions;
    if (overExt === void 0) {
      merged.extensions = baseRecord?.extensions;
    } else if (isPlainObject(overExt)) {
      if (Object.keys(overExt).length === 0) {
        merged.extensions = {};
      } else if (isPlainObject(baseRecord?.extensions)) {
        merged.extensions = mergeExtensionsMaps(
          baseRecord.extensions,
          overExt
        );
      } else {
        merged.extensions = { ...overExt };
      }
    } else {
      merged.extensions = overExt;
    }
  }
  return merged;
}
var DEFAULT_CLIENT_VERSION = "1.0.0";
var HTTP_CONNECT_TIMEOUT = 3e3;

// src/mcp-client-manager/transport-utils.ts
function normalizeHeaders2(headers) {
  if (!headers) {
    return {};
  }
  if (typeof headers === "object" && !Array.isArray(headers) && !(headers instanceof Headers)) {
    return headers;
  }
  const normalized = {};
  const headersObj = new Headers(headers);
  headersObj.forEach((value, key) => {
    normalized[key] = value;
  });
  return normalized;
}
function getExistingAuthorization(headers) {
  return headers["Authorization"] ?? headers["authorization"];
}
function buildRequestInit(accessToken, requestInit) {
  if (!accessToken) {
    return requestInit;
  }
  const existingHeaders = normalizeHeaders2(requestInit?.headers);
  const existingAuth = getExistingAuthorization(existingHeaders);
  const { authorization: _, ...headersWithoutLowercaseAuth } = existingHeaders;
  return {
    ...requestInit,
    headers: {
      Authorization: existingAuth ?? `Bearer ${accessToken}`,
      ...headersWithoutLowercaseAuth
    }
  };
}
function wrapTransportForLogging(serverId, logger, transport) {
  class LoggingTransport {
    constructor(inner) {
      this.inner = inner;
      this.inner.onmessage = (message, extra) => {
        try {
          logger({ direction: "receive", message, serverId });
        } catch {
        }
        this.onmessage?.(message, extra);
      };
      this.inner.onclose = () => {
        this.onclose?.();
      };
      this.inner.onerror = (error) => {
        this.onerror?.(error);
      };
    }
    onclose;
    onerror;
    onmessage;
    async start() {
      if (typeof this.inner.start === "function") {
        await this.inner.start();
      }
    }
    async send(message, options) {
      try {
        logger({ direction: "send", message, serverId });
      } catch {
      }
      await this.inner.send(message, options);
    }
    async close() {
      await this.inner.close();
    }
    get sessionId() {
      return this.inner.sessionId;
    }
    setProtocolVersion(version) {
      if (typeof this.inner.setProtocolVersion === "function") {
        this.inner.setProtocolVersion(version);
      }
    }
  }
  return new LoggingTransport(transport);
}
function createDefaultRpcLogger() {
  return ({ direction, message, serverId }) => {
    let printable;
    try {
      printable = typeof message === "string" ? message : JSON.stringify(message);
    } catch {
      printable = String(message);
    }
    console.debug(`[MCP:${serverId}] ${direction.toUpperCase()} ${printable}`);
  };
}

// src/server-doctor-core.ts
function createServerDoctorResult(target, options = {}) {
  return {
    target,
    generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
    status: "ready",
    probe: null,
    connection: {
      status: "skipped",
      detail: "Connection step did not run."
    },
    initInfo: null,
    capabilities: null,
    tools: [],
    toolsMetadata: {},
    resources: [],
    resourceTemplates: [],
    prompts: [],
    checks: {
      probe: skippedCheck(options.probeDetail ?? "HTTP probe did not run."),
      connection: skippedCheck("Connection step did not run."),
      initialization: skippedCheck("Initialization info was not collected."),
      capabilities: skippedCheck("Capabilities were not collected."),
      tools: skippedCheck("Tools were not collected."),
      resources: skippedCheck("Resources were not collected."),
      resourceTemplates: skippedCheck("Resource templates were not collected."),
      prompts: skippedCheck("Prompts were not collected.")
    },
    error: null
  };
}
function applyConnectedServerDoctorState(result, collected) {
  result.connection = {
    status: "connected",
    detail: "Connected and initialized successfully."
  };
  result.checks.connection = okCheck(result.connection.detail);
  result.initInfo = collected.initInfo;
  result.capabilities = collected.capabilities;
  result.tools = collected.tools;
  result.toolsMetadata = collected.toolsMetadata;
  result.resources = collected.resources;
  result.resourceTemplates = collected.resourceTemplates;
  result.prompts = collected.prompts;
  result.checks.initialization = collected.checks.initialization;
  result.checks.capabilities = collected.checks.capabilities;
  result.checks.tools = collected.checks.tools;
  result.checks.resources = collected.checks.resources;
  result.checks.resourceTemplates = collected.checks.resourceTemplates;
  result.checks.prompts = collected.checks.prompts;
  if (collected.errors.length > 0) {
    result.error = collected.errors[0] ?? result.error;
  }
}
function buildConnectedServerDoctorState(input) {
  const errors = [
    input.toolsResult.error,
    input.resourcesResult.error,
    input.promptsResult.error,
    input.resourceTemplatesResult.error
  ].filter((error) => Boolean(error));
  return {
    initInfo: input.initInfo,
    capabilities: input.capabilities,
    tools: input.toolsResult.tools,
    toolsMetadata: input.toolsResult.toolsMetadata,
    resources: input.resourcesResult.resources,
    resourceTemplates: input.resourceTemplatesResult.resourceTemplates,
    prompts: input.promptsResult.prompts,
    checks: {
      initialization: input.initInfo ? okCheck("Initialization info captured.") : errorCheck(
        "Server connected but did not return initialization info."
      ),
      capabilities: input.capabilities ? okCheck("Server capabilities captured.") : errorCheck("Server connected but did not advertise capabilities."),
      tools: input.toolsResult.check,
      resources: input.resourcesResult.check,
      resourceTemplates: input.resourceTemplatesResult.check,
      prompts: input.promptsResult.check
    },
    errors
  };
}
function buildDoctorProbeConfig(config, options) {
  const accessToken = resolveProbeAccessToken(config);
  const clientCapabilities = resolveDoctorClientCapabilities(config);
  return {
    url: config.url,
    headers: normalizeHeaders2(config.requestInit?.headers),
    ...accessToken ? { accessToken } : {},
    ...clientCapabilities ? { clientCapabilities } : {},
    timeoutMs: options.timeout,
    retryPolicy: options.retryPolicy
  };
}
function normalizeServerDoctorError(error) {
  if (isServerDoctorError(error)) {
    return error;
  }
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
  const lower = message.toLowerCase();
  if (lower.includes("timed out") || lower.includes("timeout")) {
    return { code: "TIMEOUT", message };
  }
  if (lower.includes("connect") || lower.includes("connection") || lower.includes("refused") || lower.includes("econn")) {
    return { code: "SERVER_UNREACHABLE", message };
  }
  return { code: "INTERNAL_ERROR", message };
}
function resolveProbeAccessToken(config) {
  const explicitToken = config.accessToken?.trim();
  if (explicitToken) {
    return explicitToken;
  }
  const headers = normalizeHeaders2(config.requestInit?.headers);
  const authorizationHeader = getExistingAuthorization(headers);
  if (!authorizationHeader) {
    return void 0;
  }
  const match = authorizationHeader.match(/^Bearer\s+(.+)$/i);
  return match?.[1]?.trim() || void 0;
}
function resolveDoctorClientCapabilities(config) {
  return config.clientCapabilities ?? config.capabilities;
}
function hasConnectionCredentials(config, options = {}) {
  return Boolean(
    resolveProbeAccessToken(config) || config.refreshToken || (options.includeAuthProvider === false ? void 0 : config.authProvider)
  );
}
function summarizeProbeCheck(probe, hasCredentials) {
  switch (probe.status) {
    case "ready":
      return okCheck(
        `HTTP initialize probe succeeded via ${probe.transport.selected ?? "unknown transport"}.`
      );
    case "oauth_required":
      return hasCredentials ? okCheck(
        "Unauthenticated probe requires OAuth; continuing with provided credentials."
      ) : errorCheck("Server requires OAuth before it can be connected.");
    case "reachable":
      return errorCheck(
        "HTTP endpoint was reachable, but the initialize probe did not complete successfully."
      );
    case "error":
      return errorCheck(probe.error ?? "HTTP probe failed.");
    default: {
      const exhaustive = probe.status;
      return errorCheck(String(exhaustive));
    }
  }
}
function deriveDoctorStatus(result) {
  if (result.probe?.status === "oauth_required" && result.connection.status === "skipped") {
    return "oauth_required";
  }
  if (result.connection.status === "error") {
    return "error";
  }
  return Object.values(result.checks).some((check) => check.status === "error") ? "partial" : "ready";
}
function okCheck(detail) {
  return { status: "ok", detail };
}
function errorCheck(detail) {
  return { status: "error", detail };
}
function skippedCheck(detail) {
  return { status: "skipped", detail };
}
function describeCount(count, label) {
  return `${count} ${label}${count === 1 ? "" : "s"} discovered.`;
}
function isServerDoctorError(error) {
  return !!error && typeof error === "object" && typeof error.code === "string" && typeof error.message === "string";
}

// src/http-server-doctor.ts
var MAX_PAGINATION_PAGES = 1e3;
async function runHttpServerDoctor(input, dependencies = {}) {
  const probeServer = dependencies.probeServer ?? probeMcpServer;
  const connectClient = dependencies.connectClient ?? connectHttpDoctorClient;
  const result = createServerDoctorResult(input.target);
  try {
    result.probe = await probeServer(
      buildDoctorProbeConfig(input.config, {
        timeout: input.timeout,
        retryPolicy: input.retryPolicy
      })
    );
    result.checks.probe = summarizeProbeCheck(
      result.probe,
      hasConnectionCredentials(input.config)
    );
  } catch (error) {
    const structured = normalizeServerDoctorError(error);
    result.checks.probe = errorCheck(
      `HTTP probe failed: ${structured.message}`
    );
    result.error = structured;
  }
  if (result.probe?.status === "oauth_required" && !hasConnectionCredentials(input.config)) {
    result.status = "oauth_required";
    result.connection = {
      status: "skipped",
      detail: "Server requires OAuth before a connection can be established."
    };
    result.checks.connection = skippedCheck(result.connection.detail);
    result.error = {
      code: "OAUTH_REQUIRED",
      message: "Server requires OAuth before it can be connected. Run an OAuth login flow first.",
      details: {
        registrationStrategies: result.probe.oauth.registrationStrategies,
        authorizationServerMetadataUrl: result.probe.oauth.authorizationServerMetadataUrl,
        resourceMetadataUrl: result.probe.oauth.resourceMetadataUrl
      }
    };
    return result;
  }
  let client = null;
  try {
    const connectedClient = await connectClient(input.config, {
      timeout: input.timeout,
      rpcLogger: input.rpcLogger,
      retryPolicy: input.retryPolicy
    });
    client = connectedClient;
    const collected = await collectConnectedHttpServerDoctorState(
      connectedClient,
      {
        timeout: input.timeout,
        retryPolicy: input.retryPolicy
      }
    );
    applyConnectedServerDoctorState(result, collected);
  } catch (error) {
    const structured = normalizeServerDoctorError(error);
    result.connection = {
      status: "error",
      detail: structured.message
    };
    result.checks.connection = errorCheck(structured.message);
    result.error = structured;
  } finally {
    if (client !== null) {
      await safeCloseClient(client);
    }
  }
  result.status = deriveDoctorStatus(result);
  if (result.status === "ready") {
    result.error = null;
  }
  return result;
}
async function collectConnectedHttpServerDoctorState(client, options) {
  const initInfo = client.getInitializationInfo() ?? null;
  const capabilities = client.getServerCapabilities() ?? null;
  const [toolsResult, resourcesResult, promptsResult, resourceTemplatesResult] = await Promise.all([
    collectTools(client, options),
    collectResources(client, options),
    collectPrompts(client, capabilities, options),
    collectResourceTemplates(client, options)
  ]);
  return buildConnectedServerDoctorState({
    initInfo,
    capabilities,
    toolsResult,
    resourcesResult,
    promptsResult,
    resourceTemplatesResult
  });
}
async function collectTools(client, options) {
  try {
    const tools = await drainPaginatedList(
      async (cursor) => withRetry(
        () => client.listTools(
          cursor ? { cursor } : void 0,
          withTimeout(options.timeout)
        ),
        options.retryPolicy
      ).catch((error) => {
        if (isMethodUnavailableError(error, "tools/list")) {
          return { tools: [] };
        }
        throw error;
      }),
      "tools/list",
      (page) => page.tools ?? []
    );
    const toolsMetadata = {};
    const toolsWithoutMeta = tools?.map((tool) => {
      if (tool._meta !== void 0) {
        toolsMetadata[tool.name] = tool._meta;
      }
      const { _meta: _ignoredMeta, ...toolWithoutMeta } = tool;
      return toolWithoutMeta;
    }) ?? [];
    return {
      tools: toolsWithoutMeta,
      toolsMetadata,
      check: okCheck(describeCount(toolsWithoutMeta.length, "tool"))
    };
  } catch (error) {
    const structured = normalizeServerDoctorError(error);
    return {
      tools: [],
      toolsMetadata: {},
      check: errorCheck(structured.message),
      error: structured
    };
  }
}
async function collectResources(client, options) {
  try {
    const resources = await drainPaginatedList(
      async (cursor) => withRetry(
        () => client.listResources(
          cursor ? { cursor } : void 0,
          withTimeout(options.timeout)
        ),
        options.retryPolicy
      ).catch((error) => {
        if (isMethodUnavailableError(error, "resources/list")) {
          return { resources: [] };
        }
        throw error;
      }),
      "resources/list",
      (page) => page.resources ?? []
    );
    return {
      resources,
      check: okCheck(describeCount(resources.length, "resource"))
    };
  } catch (error) {
    const structured = normalizeServerDoctorError(error);
    return {
      resources: [],
      check: errorCheck(structured.message),
      error: structured
    };
  }
}
async function collectPrompts(client, capabilities, options) {
  try {
    const promptCapabilities = capabilities && typeof capabilities === "object" && "prompts" in capabilities ? capabilities.prompts : void 0;
    if (promptCapabilities === void 0 && capabilities) {
      return {
        prompts: [],
        check: okCheck(describeCount(0, "prompt"))
      };
    }
    const prompts = await drainPaginatedList(
      async (cursor) => withRetry(
        () => client.listPrompts(
          cursor ? { cursor } : void 0,
          withTimeout(options.timeout)
        ),
        options.retryPolicy
      ).catch((error) => {
        if (isMethodUnavailableError(error, "prompts/list")) {
          return { prompts: [] };
        }
        throw error;
      }),
      "prompts/list",
      (page) => page.prompts ?? []
    );
    return {
      prompts,
      check: okCheck(describeCount(prompts.length, "prompt"))
    };
  } catch (error) {
    const structured = normalizeServerDoctorError(error);
    return {
      prompts: [],
      check: errorCheck(structured.message),
      error: structured
    };
  }
}
async function collectResourceTemplates(client, options) {
  try {
    let unsupported = false;
    const resourceTemplates = await drainPaginatedList(
      async (cursor) => withRetry(
        () => client.listResourceTemplates(
          cursor ? { cursor } : void 0,
          withTimeout(options.timeout)
        ),
        options.retryPolicy
      ).catch((error) => {
        if (isMethodUnavailableError(error, "resources/templates") || (error instanceof Error ? error.message : String(error)).toLowerCase().includes("resources/templates")) {
          unsupported = true;
          return { resourceTemplates: [] };
        }
        throw error;
      }),
      "resources/templates",
      (page) => page.resourceTemplates ?? []
    );
    return unsupported ? {
      resourceTemplates,
      check: skippedCheck("Server does not support resources/templates.")
    } : {
      resourceTemplates,
      check: okCheck(
        describeCount(resourceTemplates.length, "resource template")
      )
    };
  } catch (error) {
    const structured = normalizeServerDoctorError(error);
    return {
      resourceTemplates: [],
      check: errorCheck(structured.message),
      error: structured
    };
  }
}
async function connectHttpDoctorClient(config, options) {
  if (config.refreshToken) {
    throw new Error(
      "Browser-safe HTTP doctor does not support refreshToken-based authentication."
    );
  }
  const client = new Client(
    {
      name: "mcpjam",
      version: config.version ?? DEFAULT_CLIENT_VERSION
    },
    {
      capabilities: config.clientCapabilities ? normalizeClientCapabilities(config.clientCapabilities) : mergeClientCapabilities(
        getDefaultClientCapabilities(),
        config.capabilities
      )
    }
  );
  if (config.onError) {
    client.onerror = (error) => config.onError?.(error);
  }
  try {
    const transport = await connectViaHttp(config, client, options);
    if (transport) {
      const statefulClient = client;
      return {
        close: client.close.bind(client),
        getInitializationInfo: () => statefulClient.getInitializationInfo?.() ?? null,
        getServerCapabilities: () => statefulClient.getServerCapabilities?.() ?? null,
        listPrompts: client.listPrompts.bind(client),
        listResourceTemplates: client.listResourceTemplates.bind(client),
        listResources: client.listResources.bind(client),
        listTools: client.listTools.bind(client)
      };
    }
    throw new Error("Failed to connect to HTTP MCP server.");
  } catch (error) {
    await safeCloseClient(client);
    throw error;
  }
}
async function connectViaHttp(config, client, options) {
  const url = new URL(config.url);
  const requestInit = buildRequestInit(
    config.accessToken?.trim() || void 0,
    config.requestInit
  );
  const preferSSE = config.preferSSE ?? url.pathname.endsWith("/sse");
  const logger = resolveRpcLogger(config, options.rpcLogger);
  let streamableError;
  if (!preferSSE) {
    const streamableTransport = new StreamableHTTPClientTransport(url, {
      requestInit,
      reconnectionOptions: config.reconnectionOptions,
      authProvider: config.authProvider,
      sessionId: config.sessionId
    });
    try {
      const wrapped = logger ? wrapTransportForLogging("http-doctor", logger, streamableTransport) : streamableTransport;
      await client.connect(wrapped, {
        timeout: Math.min(options.timeout, HTTP_CONNECT_TIMEOUT)
      });
      return streamableTransport;
    } catch (error) {
      streamableError = error;
      await safeCloseTransport(streamableTransport);
    }
  }
  const sseTransport = new SSEClientTransport(url, {
    requestInit,
    eventSourceInit: config.eventSourceInit,
    authProvider: config.authProvider
  });
  try {
    const wrapped = logger ? wrapTransportForLogging("http-doctor", logger, sseTransport) : sseTransport;
    await client.connect(wrapped, { timeout: options.timeout });
    return sseTransport;
  } catch (error) {
    await safeCloseTransport(sseTransport);
    if (streamableError) {
      const streamableMessage = streamableError instanceof Error ? streamableError.message : String(streamableError);
      const sseMessage = error instanceof Error ? error.message : String(error);
      throw new Error(
        `Streamable HTTP error: ${streamableMessage}. SSE error: ${sseMessage}`
      );
    }
    throw error;
  }
}
function resolveRpcLogger(config, rpcLogger) {
  if (rpcLogger) {
    return rpcLogger;
  }
  if (config.rpcLogger) {
    return config.rpcLogger;
  }
  if (config.logJsonRpc) {
    return createDefaultRpcLogger();
  }
  return void 0;
}
async function safeCloseClient(client) {
  try {
    await client.close();
  } catch {
  }
}
async function safeCloseTransport(transport) {
  try {
    await transport.close();
  } catch {
  }
}
async function drainPaginatedList(fetchPage, methodName, selectItems) {
  const items = [];
  let cursor;
  let pages = 0;
  while (pages < MAX_PAGINATION_PAGES) {
    const page = await fetchPage(cursor);
    items.push(...selectItems(page));
    pages += 1;
    if (!page.nextCursor) {
      return items;
    }
    cursor = page.nextCursor;
  }
  throw new Error(
    `${methodName} exceeded ${MAX_PAGINATION_PAGES} pages while collecting doctor state.`
  );
}
function withTimeout(timeout) {
  return { timeout };
}
function withRetry(operation, retryPolicy) {
  return retryWithPolicy({
    policy: retryPolicy,
    operation: () => operation(),
    shouldRetryError: (error) => isRetryableTransientError(error)
  });
}

export { probeMcpServer, redactSensitiveValue, runHttpServerDoctor };
//# sourceMappingURL=worker.js.map
//# sourceMappingURL=worker.js.map