{"record":{"id":"08b4664821e8db71","repo":"charmbracelet/crush","slug":"url-w","errorCode":null,"errorMessage":"url: %w","messagePattern":"url: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/config.go","lineNumber":482,"sourceCode":"\n// ResolvedURL returns m.URL expanded through the given resolver. The\n// receiver is not mutated. Errors from the resolver are already\n// sanitized by ResolveValue and are wrapped with %w for errors.Is/As.\n//\n// URLs run through the same shell-expansion pipeline as the other\n// fields, so a literal '$' (e.g. OData query strings containing\n// $filter/$select) must be escaped as '\\$' or '${DOLLAR:-$}' to avoid\n// being interpreted as a variable reference. Same constraint already\n// applies to command, args, env, and headers.\n//\n// See ResolvedEnv for guidance on picking a resolver.\nfunc (m MCPConfig) ResolvedURL(r VariableResolver) (string, error) {\n\tif m.URL == \"\" {\n\t\treturn \"\", nil\n\t}\n\tv, err := r.ResolveValue(m.URL)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"url: %w\", err)\n\t}\n\treturn v, nil\n}\n\n// knownSessionlessMCPs is the set of MCP endpoint URLs (normalized, no\n// trailing slash) that are known not to maintain an MCP session — they\n// never issue a Mcp-Session-Id and reject the SEP-2575\n// \"subscriptions/listen\" stream. Add an entry when a server is confirmed to\n// behave this way.\nvar knownSessionlessMCPs = map[string]struct{}{\n\t\"https://api.github.com/mcp\":        {},\n\t\"https://api.githubcopilot.com/mcp\": {},\n}\n\n// IsSessionless reports whether the server should be treated as sessionless.\n// An explicit Sessionless value wins; when unset, the resolved URL is matched\n// against knownSessionlessMCPs (trailing slash ignored). The URL is resolved\n// through r so $VAR-expanded endpoints are detected too; on a resolution","sourceCodeStart":464,"sourceCodeEnd":500,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/config.go#L464-L500","documentation":"MCPConfig.ResolvedURL resolves the MCP server's remote endpoint URL through VariableResolver.ResolveValue, expanding template variables in the configured URL string. Any resolver failure is wrapped as `url: <cause>` — again typically an undefined environment variable or malformed reference inside {{...}}.","triggerScenarios":"Loading/resolving an MCP config with a `url` field containing an unresolvable variable, e.g. url = \"https://{{env.MCP_HOST}}/mcp\" where MCP_HOST is unset, or an invalid template reference.","commonSituations":"Remote (HTTP/SSE) MCP endpoints configured with host/token fragments sourced from env vars missing in the current environment; CI runners without the secret; typo in the variable name.","solutions":["Read the wrapped cause to see which variable failed to resolve.","Export the referenced environment variable in the environment where crush runs.","Alternatively hardcode the URL or supply the value through crush's secrets configuration.","Correct the {{...}} reference spelling in the MCP url in crushrc."],"exampleFix":"// before (crushrc)\nmcp remote --url 'https://{{env.MCP_HOST}}/mcp' // MCP_HOST unset\n// after\nexport MCP_HOST=mcp.example.com  # then rerun crush","handlingStrategy":"validation","validationCode":"# ensure env vars used in MCP urls are set before loading config\ngrep -o '{{env\\.[A-Z_]*}}' crushrc | sed 's/{{env\\.//;s/}}//' | sort -u | while read -r v; do\n  [ -n \"${!v+x}\" ] || echo \"missing env var: $v\"\ndone","typeGuard":null,"tryCatchPattern":"url, err := m.ResolvedURL(resolver)\nif err != nil {\n    return fmt.Errorf(\"cannot resolve MCP endpoint url: %w\", err)\n}","preventionTips":["Export MCP host/token env vars wherever crush runs, including CI.","Keep URLs simple; template only the pieces that truly vary by environment.","Re-test remote MCP configs after rotating secrets or renaming vars.","Document required env vars next to the MCP block in crushrc."],"tags":["config","mcp","variable-resolution","url","env"],"backgroundTag":"unresolved-config-variable","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}