charmbracelet/crush · error

failed to refresh MCP tools: %w

Error message

failed to refresh MCP tools: %w

What it means

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.

Source

Thrown at internal/client/config.go:295

func (c *Client) DisableDockerMCP(ctx context.Context, id string) error {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/docker/disable", id), nil, nil, nil)
	if err != nil {
		return fmt.Errorf("failed to disable docker MCP: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to disable docker MCP: status code %d", rsp.StatusCode)
	}
	return nil
}

// RefreshMCPTools refreshes tools for a named MCP server.
func (c *Client) RefreshMCPTools(ctx context.Context, id, name string) error {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/refresh-tools", id), nil, jsonBody(struct {
		Name string `json:"name"`
	}{Name: name}), http.Header{"Content-Type": []string{"application/json"}})
	if err != nil {
		return fmt.Errorf("failed to refresh MCP tools: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to refresh MCP tools: status code %d", rsp.StatusCode)
	}
	return nil
}

// ReadMCPResource reads a resource from a named MCP server.
func (c *Client) ReadMCPResource(ctx context.Context, id, name, uri string) ([]MCPResourceContents, error) {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/read-resource", id), nil, jsonBody(struct {
		Name string `json:"name"`
		URI  string `json:"uri"`
	}{Name: name, URI: uri}), http.Header{"Content-Type": []string{"application/json"}})
	if err != nil {
		return nil, fmt.Errorf("failed to read MCP resource: %w", err)
	}
	defer rsp.Body.Close()

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Verify the Coder URL and network connectivity (curl the server's / or health endpoint).
  2. Confirm the workspace ID exists via the API (GET /workspaces) before calling RefreshMCPTools.
  3. Inspect the wrapped error (%w) for the root cause — connection refused vs DNS vs TLS.
  4. Retry with a fresh context if the context was cancelled or timed out.

Example fix

// before
if err := client.RefreshMCPTools(ctx, workspaceID, "my-mcp"); err != nil {
    log.Fatal(err)
}
// after
if err := client.RefreshMCPTools(ctx, workspaceID, "my-mcp"); err != nil {
    var statusErr interface{ HTTPStatus() int }
    if errors.As(err, &wrapped) && strings.Contains(err.Error(), "connection refused") {
        log.Printf("coder server unreachable, retrying later: %v", err)
        return
    }
    return fmt.Errorf("refresh MCP tools for %q: %w", "my-mcp", err)
}
Defensive patterns

Strategy: retry

Validate before calling

func validateRefreshInput(workspaceID, mcpName string) error {
    if workspaceID == "" || mcpName == "" {
        return errors.New("workspace ID and MCP name are required")
    }
    return nil
}

Try / catch

err := client.RefreshMCPTools(ctx, id, name)
if err != nil {
    if strings.Contains(err.Error(), "connection refused") || errors.Is(ctx.Err(), context.DeadlineExceeded) {
        // transient: retry with backoff
    }
    return fmt.Errorf("refresh MCP tools %q: %w", name, err)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/1a0e8867e12c21a1. Report an issue: GitHub.