{"record":{"id":"1a0e8867e12c21a1","repo":"charmbracelet/crush","slug":"failed-to-refresh-mcp-tools-w","errorCode":null,"errorMessage":"failed to refresh MCP tools: %w","messagePattern":"failed to refresh MCP tools: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/config.go","lineNumber":295,"sourceCode":"func (c *Client) DisableDockerMCP(ctx context.Context, id string) error {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/mcp/docker/disable\", id), nil, nil, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to disable docker MCP: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"failed to disable docker MCP: status code %d\", rsp.StatusCode)\n\t}\n\treturn nil\n}\n\n// RefreshMCPTools refreshes tools for a named MCP server.\nfunc (c *Client) RefreshMCPTools(ctx context.Context, id, name string) error {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/mcp/refresh-tools\", id), nil, jsonBody(struct {\n\t\tName string `json:\"name\"`\n\t}{Name: name}), http.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to refresh MCP tools: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"failed to refresh MCP tools: status code %d\", rsp.StatusCode)\n\t}\n\treturn nil\n}\n\n// ReadMCPResource reads a resource from a named MCP server.\nfunc (c *Client) ReadMCPResource(ctx context.Context, id, name, uri string) ([]MCPResourceContents, error) {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/mcp/read-resource\", id), nil, jsonBody(struct {\n\t\tName string `json:\"name\"`\n\t\tURI  string `json:\"uri\"`\n\t}{Name: name, URI: uri}), http.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read MCP resource: %w\", err)\n\t}\n\tdefer rsp.Body.Close()","sourceCodeStart":277,"sourceCodeEnd":313,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/config.go#L277-L313","documentation":"RefreshMCPTools sends a POST to /workspaces/{id}/mcp/refresh-tools to re-fetch the tool list of a named MCP server. The client wraps any transport-level error from c.post with 'failed to refresh MCP tools: %w'. It is thrown before any status code is checked, so it means the HTTP request itself could not be completed.","triggerScenarios":"c.post returns an error: DNS failure, connection refused, TLS error, invalid workspace ID producing a malformed URL, cancelled/expired context, or failure building the request body.","commonSituations":"Coder server not running or wrong URL configured; network outage or VPN down; workspace ID typo; client-side proxy misconfiguration; the underlying MCP server crashed and the daemon endpoint is unreachable.","solutions":["Verify the Coder URL and network connectivity (curl the server's / or health endpoint).","Confirm the workspace ID exists via the API (GET /workspaces) before calling RefreshMCPTools.","Inspect the wrapped error (%w) for the root cause — connection refused vs DNS vs TLS.","Retry with a fresh context if the context was cancelled or timed out."],"exampleFix":"// before\nif err := client.RefreshMCPTools(ctx, workspaceID, \"my-mcp\"); err != nil {\n    log.Fatal(err)\n}\n// after\nif err := client.RefreshMCPTools(ctx, workspaceID, \"my-mcp\"); err != nil {\n    var statusErr interface{ HTTPStatus() int }\n    if errors.As(err, &wrapped) && strings.Contains(err.Error(), \"connection refused\") {\n        log.Printf(\"coder server unreachable, retrying later: %v\", err)\n        return\n    }\n    return fmt.Errorf(\"refresh MCP tools for %q: %w\", \"my-mcp\", err)\n}","handlingStrategy":"retry","validationCode":"func validateRefreshInput(workspaceID, mcpName string) error {\n    if workspaceID == \"\" || mcpName == \"\" {\n        return errors.New(\"workspace ID and MCP name are required\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"err := client.RefreshMCPTools(ctx, id, name)\nif err != nil {\n    if strings.Contains(err.Error(), \"connection refused\") || errors.Is(ctx.Err(), context.DeadlineExceeded) {\n        // transient: retry with backoff\n    }\n    return fmt.Errorf(\"refresh MCP tools %q: %w\", name, err)\n}","preventionTips":["Always wrap the call and log the wrapped root cause, not just the outer message.","Health-check the Coder server before batching tool refreshes.","Use a context with a sensible timeout instead of context.Background().","Verify workspace ID and MCP server name against a prior list call."],"tags":["network","http-client","mcp"],"backgroundTag":"http-request-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}