charmbracelet/crush · error

failed to read MCP resource: %w

Error message

failed to read MCP resource: %w

What it means

ReadMCPResource POSTs to /workspaces/{id}/mcp/read-resource to fetch a resource from a named MCP server and returns its contents. The error wraps any transport failure from c.post before the response status is inspected — the request never completed successfully.

Source

Thrown at internal/client/config.go:311

	}{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()
	if rsp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to read MCP resource: status code %d", rsp.StatusCode)
	}
	var contents []MCPResourceContents
	if err := json.NewDecoder(rsp.Body).Decode(&contents); err != nil {
		return nil, fmt.Errorf("failed to decode MCP resource: %w", err)
	}
	return contents, nil
}

func (c *Client) ListMCPPrompts(ctx context.Context, id string) ([]proto.MCPPrompt, error) {
	rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/mcp/prompts", id), nil, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to list MCP prompts: %w", err)
	}
	defer rsp.Body.Close()

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Check connectivity to the Coder server and the configured base URL.
  2. Validate the workspace ID used in the path.
  3. Examine the wrapped error for the exact network cause.
  4. Increase the context timeout for large or slow resources and retry.

Example fix

// before
contents, err := client.ReadMCPResource(ctx, id, name, uri)
// after
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
contents, err := client.ReadMCPResource(ctx, id, name, uri)
if err != nil {
    return nil, fmt.Errorf("read resource %s from %s: %w", uri, name, err)
}
Defensive patterns

Strategy: retry

Validate before calling

func validateReadInput(workspaceID, server, uri string) error {
    if workspaceID == "" || server == "" || uri == "" {
        return errors.New("workspace ID, server name, and URI are required")
    }
    if !strings.Contains(uri, ":") {
        return fmt.Errorf("resource URI %q is not a valid URI", uri)
    }
    return nil
}

Try / catch

ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
contents, err := client.ReadMCPResource(ctx, id, name, uri)
if err != nil {
    if isTransient(err) { // network-level errors are retryable
        contents, err = client.ReadMCPResource(ctx, id, name, uri)
    }
    if err != nil { return nil, fmt.Errorf("read %s: %w", uri, err) }
}

Prevention

When it happens

Trigger: c.post fails: unreachable host, DNS/TLS errors, malformed workspace ID in the URL path, cancelled context, or request serialization failure.

Common situations: Coder daemon down; wrong base URL in client config; offline/VPN environment; context deadline exceeded while the MCP server was slow.

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/7c4968b434eaa26a. Report an issue: GitHub.