upstash/context7 · error

Pass the Context7 deployment root, without /mcp or /api

Error message

Pass the Context7 deployment root, without /mcp or /api

What it means

Thrown by normalizeDeploymentBaseUrl when the URL path already ends in /mcp or /api. The CLI derives endpoint URLs itself (getMcpUrl appends /mcp), so passing a full endpoint URL would yield doubled paths like /mcp/mcp; the user must pass the deployment root instead.

Solutions

  1. Drop the trailing /mcp or /api and pass the deployment root: 'https://host'.
  2. If you copied the URL from an MCP client, use the server's base origin instead.
  3. Omit the argument to use the hosted default.

Example fix

// before
ctx7 setup --url https://my-onprem.internal/mcp

// after
ctx7 setup --url https://my-onprem.internal
Defensive patterns

Strategy: validation

Validate before calling

function isDeploymentRoot(raw: string): boolean {
  try {
    const p = new URL(raw).pathname.replace(/\/+$/, '');
    return !p.endsWith('/mcp') && !p.endsWith('/api');
  } catch { return false; }
}

Try / catch

try {
  const dep = resolveSetupDeployment(input);
} catch (e) {
  if ((e as Error).message.includes('without /mcp or /api')) {
    console.error('Pass the deployment root, e.g. https://host — the CLI appends /mcp itself.');
  }
}

Prevention

When it happens

Trigger: normalizeDeploymentBaseUrl receives a URL whose pathname ends with '/mcp' or '/api', e.g. 'https://host/mcp' or 'https://host/api', after trailing slashes are stripped.

Common situations: Copying the MCP endpoint URL shown in an MCP client config into the setup command; copying an API docs URL instead of the deployment origin.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of upstash/context7@4416fb855b (2026-09-16). Data as JSON: /api/errors/6f4845baa9b1b569. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/setup/deployment.ts:34

    url = new URL(raw);
  } catch {
    throw new Error(`Invalid Context7 base URL: ${raw}`);
  }

  if (url.protocol !== "http:" && url.protocol !== "https:") {
    throw new Error("Context7 base URL must use http:// or https://");
  }
  if (url.username || url.password) {
    throw new Error("Context7 base URL must not contain credentials");
  }
  if (url.search || url.hash) {
    throw new Error("Context7 base URL must not contain a query string or fragment");
  }

  url.pathname = url.pathname.replace(/\/+$/, "") || "/";
  const normalized = url.toString().replace(/\/$/, "");
  if (url.pathname.endsWith("/mcp") || url.pathname.endsWith("/api")) {
    throw new Error("Pass the Context7 deployment root, without /mcp or /api");
  }
  return normalized;
}

export function resolveSetupDeployment(input?: string): SetupDeployment {
  const baseUrl = normalizeDeploymentBaseUrl(input);
  return baseUrl === DEFAULT_CONTEXT7_BASE_URL
    ? { kind: "hosted", baseUrl: DEFAULT_CONTEXT7_BASE_URL }
    : { kind: "custom", baseUrl };
}

export function getMcpUrl(deployment: SetupDeployment, auth: AuthOptions): string {
  if (deployment.kind === "hosted") {
    return auth.mode === "oauth"
      ? `${HOSTED_MCP_BASE_URL}/mcp/oauth`
      : `${HOSTED_MCP_BASE_URL}/mcp`;
  }
  return `${deployment.baseUrl}/mcp`;

View on GitHub (pinned to 4416fb855b)