charmbracelet/crush · error

failed to get MCP prompt: status code %d

Error message

failed to get MCP prompt: status code %d

What it means

GetMCPPrompt requires HTTP 200 from /workspaces/{id}/mcp/get-prompt. Any other status yields 'failed to get MCP prompt: status code %d'. The body (which may contain the server's error explanation, e.g. invalid prompt arguments) is discarded.

Source

Thrown at internal/client/config.go:352

	if err := json.NewDecoder(rsp.Body).Decode(&prompts); err != nil {
		return nil, fmt.Errorf("failed to decode MCP prompts: %w", err)
	}
	return prompts, nil
}

// 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. Map status to cause: 404 → verify promptID/clientID; 400 → check args against the prompt's schema (ListMCPPrompts shows required args); 401 → re-authenticate.
  2. Confirm the MCP server is connected and healthy before rendering.
  3. Check server logs for 5xx details.
  4. Re-fetch the prompt list to ensure the prompt still exists.

Example fix

// before
prompt, err := client.GetMCPPrompt(ctx, id, clientID, promptID, args)
// after
prompt, err := client.GetMCPPrompt(ctx, id, clientID, promptID, args)
if err != nil && strings.Contains(err.Error(), "status code 400") {
    return "", fmt.Errorf("invalid arguments for prompt %q — verify required args: %w", promptID, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the prompt exists and its required args beforehand
prompts, err := client.ListMCPPrompts(ctx, workspaceID)
if err != nil {
    return err
}
var found *proto.MCPPrompt
for i := range prompts {
    if prompts[i].ID == promptID { found = &prompts[i] }
}
if found == nil {
    return fmt.Errorf("prompt %q does not exist; cannot call GetMCPPrompt", promptID)
}
// validate args against found schema before calling

Try / catch

prompt, err := client.GetMCPPrompt(ctx, id, clientID, promptID, args)
if err != nil {
    var code int
    if n, _ := fmt.Sscanf(err.Error(), "failed to get MCP prompt: status code %d", &code); n == 1 {
        switch {
        case code == 400:
            return "", fmt.Errorf("invalid args for prompt %q", promptID)
        case code == 401 || code == 403:
            // re-authenticate and retry
        case code == 404:
            return "", fmt.Errorf("prompt %q or client %q not found", promptID, clientID)
        }
    }
    return "", err
}

Prevention

When it happens

Trigger: 401/403 (auth), 404 (unknown prompt ID, client ID, or workspace), 400 (invalid args for the prompt's schema), 500 (MCP server failed to render the prompt).

Common situations: Prompt ID typo; passing arguments that violate the prompt's declared schema; MCP server disconnected so the backend cannot render; expired session token.

Related errors


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