{"record":{"id":"6f4845baa9b1b569","repo":"upstash/context7","slug":"pass-the-context7-deployment-root-without-mcp-or-api","errorCode":null,"errorMessage":"Pass the Context7 deployment root, without /mcp or /api","messagePattern":"Pass the Context7 deployment root, without /mcp or /api","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/setup/deployment.ts","lineNumber":34,"sourceCode":"    url = new URL(raw);\n  } catch {\n    throw new Error(`Invalid Context7 base URL: ${raw}`);\n  }\n\n  if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n    throw new Error(\"Context7 base URL must use http:// or https://\");\n  }\n  if (url.username || url.password) {\n    throw new Error(\"Context7 base URL must not contain credentials\");\n  }\n  if (url.search || url.hash) {\n    throw new Error(\"Context7 base URL must not contain a query string or fragment\");\n  }\n\n  url.pathname = url.pathname.replace(/\\/+$/, \"\") || \"/\";\n  const normalized = url.toString().replace(/\\/$/, \"\");\n  if (url.pathname.endsWith(\"/mcp\") || url.pathname.endsWith(\"/api\")) {\n    throw new Error(\"Pass the Context7 deployment root, without /mcp or /api\");\n  }\n  return normalized;\n}\n\nexport function resolveSetupDeployment(input?: string): SetupDeployment {\n  const baseUrl = normalizeDeploymentBaseUrl(input);\n  return baseUrl === DEFAULT_CONTEXT7_BASE_URL\n    ? { kind: \"hosted\", baseUrl: DEFAULT_CONTEXT7_BASE_URL }\n    : { kind: \"custom\", baseUrl };\n}\n\nexport function getMcpUrl(deployment: SetupDeployment, auth: AuthOptions): string {\n  if (deployment.kind === \"hosted\") {\n    return auth.mode === \"oauth\"\n      ? `${HOSTED_MCP_BASE_URL}/mcp/oauth`\n      : `${HOSTED_MCP_BASE_URL}/mcp`;\n  }\n  return `${deployment.baseUrl}/mcp`;","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/upstash/context7/blob/4416fb855b8f752be735e34f943b5d0762701aad/packages/cli/src/setup/deployment.ts#L16-L52","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Drop the trailing /mcp or /api and pass the deployment root: 'https://host'.","If you copied the URL from an MCP client, use the server's base origin instead.","Omit the argument to use the hosted default."],"exampleFix":"// before\nctx7 setup --url https://my-onprem.internal/mcp\n\n// after\nctx7 setup --url https://my-onprem.internal","handlingStrategy":"validation","validationCode":"function isDeploymentRoot(raw: string): boolean {\n  try {\n    const p = new URL(raw).pathname.replace(/\\/+$/, '');\n    return !p.endsWith('/mcp') && !p.endsWith('/api');\n  } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const dep = resolveSetupDeployment(input);\n} catch (e) {\n  if ((e as Error).message.includes('without /mcp or /api')) {\n    console.error('Pass the deployment root, e.g. https://host — the CLI appends /mcp itself.');\n  }\n}","preventionTips":["Treat the setup URL as an origin, not an endpoint.","Don't copy endpoint URLs from MCP client configs into deployment setup.","Remember the CLI composes ${baseUrl}/mcp itself."],"tags":["url","validation","configuration","cli"],"backgroundTag":"invalid-url-format","analyzedSha":"4416fb855b8f752be735e34f943b5d0762701aad","analyzedAt":"2026-09-16T20:28:07.148Z","contentChangedAt":"2026-09-16T20:28:07.148Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}