charmbracelet/crush · error

failed to decode MCP prompt response: %w

Error message

failed to decode MCP prompt response: %w

What it means

GetMCPPrompt fetches a prompt from the MCP server over HTTP and decodes the JSON body into a struct with a single `prompt` string field. This error means the response body was not valid JSON or did not match the expected shape. It wraps the underlying decoding error with %w so the root cause (syntax error, unexpected type, empty body) is preserved.

Source

Thrown at internal/client/config.go:358

// GetMCPPrompt retrieves a prompt from a named MCP server.
func (c *Client) GetMCPPrompt(ctx context.Context, id, clientID, promptID string, args map[string]string) (string, error) {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/get-prompt", id), nil, jsonBody(struct {
		ClientID string            `json:"client_id"`
		PromptID string            `json:"prompt_id"`
		Args     map[string]string `json:"args"`
	}{ClientID: clientID, PromptID: promptID, Args: args}), http.Header{"Content-Type": []string{"application/json"}})
	if err != nil {
		return "", fmt.Errorf("failed to get MCP prompt: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("failed to get MCP prompt: status code %d", rsp.StatusCode)
	}
	var result struct {
		Prompt string `json:"prompt"`
	}
	if err := json.NewDecoder(rsp.Body).Decode(&result); err != nil {
		return "", fmt.Errorf("failed to decode MCP prompt response: %w", err)
	}
	return result.Prompt, nil
}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Inspect the wrapped error: a json.SyntaxError means the body is not JSON at all (check for a proxy/gateway intercepting the request).
  2. Verify the URL points at the correct MCP server endpoint and version that returns {"prompt": "..."}.
  3. Dump the raw response body (curl the endpoint) to see what is actually being returned.
  4. Check network infrastructure (proxies, load balancers, auth layers) that could alter or truncate the response body.

Example fix

// before: hard to see raw body on failure
var result struct {
	Prompt string `json:"prompt"`
}
if err := json.NewDecoder(rsp.Body).Decode(&result); err != nil {
	return "", fmt.Errorf("failed to decode MCP prompt response: %w", err)
}
// after: capture the body first for diagnosis
body, _ := io.ReadAll(rsp.Body)
var result struct {
	Prompt string `json:"prompt"`
}
if err := json.Unmarshal(body, &result); err != nil {
	return "", fmt.Errorf("failed to decode MCP prompt response: %w (body: %s)", err, truncate(body, 256))
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: confirm the endpoint returns JSON with a prompt field
resp, err := http.Get(mcpPromptURL)
if err == nil {
	var probe map[string]json.RawMessage
	if json.NewDecoder(resp.Body).Decode(&probe) != nil {
		log.Println("MCP prompt endpoint does not return JSON")
	}
	if _, ok := probe["prompt"]; !ok {
		log.Println("MCP prompt endpoint response missing 'prompt' field")
	}
	resp.Body.Close()
}

Type guard

func isDecodeError(err error) bool {
	var syn *json.SyntaxError
	var typ *json.UnmarshalTypeError
	return errors.As(err, &syn) || errors.As(err, &typ)
}

Try / catch

prompt, err := client.GetMCPPrompt(ctx, name)
if err != nil {
	var syn *json.SyntaxError
	if errors.As(err, &syn) {
		// Body wasn't JSON: likely a proxy or wrong endpoint
		return fallbackPrompt(name)
	}
	return fmt.Errorf("getting MCP prompt: %w", err)
}

Prevention

When it happens

Trigger: Calling GetMCPPrompt when the server returns a 2xx response whose body is not decodable JSON containing a string `prompt` field — e.g. an HTML error page, empty body, truncated response, or a JSON object missing the `prompt` key.

Common situations: A reverse proxy or gateway returns an HTML error page with 200; the MCP server is an incompatible/older version returning a different schema; the response is cut off mid-stream; a misconfigured endpoint points at a non-MCP service.

Understand the failure class

Related errors


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