Skip to content

Commit 00e50ce

Browse files
authored
fix: properly format prompt retrieval error messages from MCP (#216)
1 parent 020ff23 commit 00e50ce

File tree

3 files changed

+151
-3
lines changed

3 files changed

+151
-3
lines changed

crates/chat-cli/src/cli/chat/cli/prompts.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crossterm::{
1919
use thiserror::Error;
2020
use unicode_width::UnicodeWidthStr;
2121

22+
use crate::cli::chat::error_formatter::format_mcp_error;
2223
use crate::cli::chat::tool_manager::PromptBundle;
2324
use crate::cli::chat::{
2425
ChatError,
@@ -278,9 +279,7 @@ impl PromptsSubcommand {
278279
style::SetAttribute(Attribute::Reset),
279280
style::Print("\n"),
280281
style::SetForegroundColor(Color::Red),
281-
style::Print(
282-
serde_json::to_string_pretty(&to_display).unwrap_or_else(|_| format!("{:?}", &to_display))
283-
),
282+
style::Print(format_mcp_error(&to_display)),
284283
style::SetForegroundColor(Color::Reset),
285284
style::Print("\n"),
286285
)?;
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/// Formats an MCP error message to be more user-friendly.
2+
///
3+
/// This function extracts nested JSON from the error message and formats it
4+
/// with proper indentation and newlines.
5+
///
6+
/// # Arguments
7+
///
8+
/// * `err` - A reference to a serde_json::Value containing the error information
9+
///
10+
/// # Returns
11+
///
12+
/// A formatted string representation of the error message
13+
pub fn format_mcp_error(err: &serde_json::Value) -> String {
14+
// Extract the message field from the error JSON
15+
if let Some(message) = err.get("message").and_then(|m| m.as_str()) {
16+
// Check if the message contains a nested JSON array
17+
if let Some(start_idx) = message.find('[') {
18+
if let Some(end_idx) = message.rfind(']') {
19+
let prefix = &message[..start_idx].trim();
20+
let nested_json = &message[start_idx..=end_idx];
21+
22+
// Try to parse the nested JSON
23+
if let Ok(nested_value) = serde_json::from_str::<serde_json::Value>(nested_json) {
24+
// Format the error message with the prefix and pretty-printed nested JSON
25+
return format!(
26+
"{}\n{}",
27+
prefix,
28+
serde_json::to_string_pretty(&nested_value).unwrap_or_else(|_| nested_json.to_string())
29+
);
30+
}
31+
}
32+
}
33+
}
34+
35+
// Fallback if message field is missing or if we couldn't extract and parse nested JSON
36+
serde_json::to_string_pretty(err).unwrap_or_else(|_| format!("{:?}", err))
37+
}
38+
39+
#[cfg(test)]
40+
mod tests {
41+
use serde_json::json;
42+
43+
use super::*;
44+
45+
#[test]
46+
fn test_format_mcp_error_with_nested_json() {
47+
let error = json!({
48+
"code": -32602,
49+
"message": "MCP error -32602: Invalid arguments for prompt agent_script_coco_was_sev2_ticket_details_retrieve: [\n {\n \"code\": \"invalid_type\",\n \"expected\": \"object\",\n \"received\": \"undefined\",\n \"path\": [],\n \"message\": \"Required\"\n }\n]"
50+
});
51+
52+
let formatted = format_mcp_error(&error);
53+
54+
// Extract the prefix and JSON part from the formatted string
55+
let parts: Vec<&str> = formatted.split('\n').collect();
56+
let prefix = parts[0];
57+
let json_part = &formatted[prefix.len() + 1..];
58+
59+
// Check that the prefix is correct
60+
assert_eq!(
61+
prefix,
62+
"MCP error -32602: Invalid arguments for prompt agent_script_coco_was_sev2_ticket_details_retrieve:"
63+
);
64+
65+
// Parse the JSON part to compare the actual content rather than the exact string
66+
let parsed_json: serde_json::Value = serde_json::from_str(json_part).expect("Failed to parse JSON part");
67+
68+
// Expected JSON structure
69+
let expected_json = json!([
70+
{
71+
"code": "invalid_type",
72+
"expected": "object",
73+
"received": "undefined",
74+
"path": [],
75+
"message": "Required"
76+
}
77+
]);
78+
79+
// Compare the parsed JSON values
80+
assert_eq!(parsed_json, expected_json);
81+
}
82+
83+
#[test]
84+
fn test_format_mcp_error_without_nested_json() {
85+
let error = json!({
86+
"code": -32602,
87+
"message": "MCP error -32602: Invalid arguments for prompt"
88+
});
89+
90+
let formatted = format_mcp_error(&error);
91+
92+
assert_eq!(
93+
formatted,
94+
"{\n \"code\": -32602,\n \"message\": \"MCP error -32602: Invalid arguments for prompt\"\n}"
95+
);
96+
}
97+
98+
#[test]
99+
fn test_format_mcp_error_non_mcp_error() {
100+
let error = json!({
101+
"error": "Unknown error occurred"
102+
});
103+
104+
let formatted = format_mcp_error(&error);
105+
106+
// Should pretty-print the entire error
107+
assert_eq!(formatted, "{\n \"error\": \"Unknown error occurred\"\n}");
108+
}
109+
110+
#[test]
111+
fn test_format_mcp_error_empty_message() {
112+
let error = json!({
113+
"code": -32602,
114+
"message": ""
115+
});
116+
117+
let formatted = format_mcp_error(&error);
118+
119+
assert_eq!(formatted, "{\n \"code\": -32602,\n \"message\": \"\"\n}");
120+
}
121+
122+
#[test]
123+
fn test_format_mcp_error_missing_message() {
124+
let error = json!({
125+
"code": -32602
126+
});
127+
128+
let formatted = format_mcp_error(&error);
129+
130+
assert_eq!(formatted, "{\n \"code\": -32602\n}");
131+
}
132+
133+
#[test]
134+
fn test_format_mcp_error_malformed_nested_json() {
135+
let error = json!({
136+
"code": -32602,
137+
"message": "MCP error -32602: Invalid arguments for prompt: [{\n \"code\": \"invalid_type\",\n \"expected\": \"object\",\n \"received\": \"undefined\",\n \"path\": [],\n \"message\": \"Required\"\n"
138+
});
139+
140+
let formatted = format_mcp_error(&error);
141+
142+
// Should return the pretty-printed JSON since the nested JSON is malformed
143+
assert_eq!(
144+
formatted,
145+
"{\n \"code\": -32602,\n \"message\": \"MCP error -32602: Invalid arguments for prompt: [{\\n \\\"code\\\": \\\"invalid_type\\\",\\n \\\"expected\\\": \\\"object\\\",\\n \\\"received\\\": \\\"undefined\\\",\\n \\\"path\\\": [],\\n \\\"message\\\": \\\"Required\\\"\\n\"\n}"
146+
);
147+
}
148+
}

crates/chat-cli/src/cli/chat/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod cli;
22
mod consts;
33
mod context;
44
mod conversation;
5+
mod error_formatter;
56
mod input_source;
67
mod message;
78
mod parse;

0 commit comments

Comments
 (0)