Skip to content

Commit f34c302

Browse files
Copilothalter73
andcommitted
Deserialize error data and populate exception.Data on client
Co-authored-by: halter73 <54385+halter73@users.noreply.github.com>
1 parent 688a956 commit f34c302

2 files changed

Lines changed: 67 additions & 1 deletion

File tree

src/ModelContextProtocol.Core/McpSessionHandler.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,29 @@ public async Task<JsonRpcResponse> SendRequestAsync(JsonRpcRequest request, Canc
453453
if (response is JsonRpcError error)
454454
{
455455
LogSendingRequestFailed(EndpointName, request.Method, error.Error.Message, error.Error.Code);
456-
throw new McpProtocolException($"Request failed (remote): {error.Error.Message}", (McpErrorCode)error.Error.Code);
456+
var exception = new McpProtocolException($"Request failed (remote): {error.Error.Message}", (McpErrorCode)error.Error.Code);
457+
458+
// Populate exception.Data with the error data if present
459+
if (error.Error.Data is not null)
460+
{
461+
// The data could be a Dictionary<string, JsonElement> or a JsonElement containing an object
462+
if (error.Error.Data is Dictionary<string, JsonElement> dataDict)
463+
{
464+
foreach (var kvp in dataDict)
465+
{
466+
exception.Data[kvp.Key] = kvp.Value;
467+
}
468+
}
469+
else if (error.Error.Data is JsonElement jsonElement && jsonElement.ValueKind == JsonValueKind.Object)
470+
{
471+
foreach (var property in jsonElement.EnumerateObject())
472+
{
473+
exception.Data[property.Name] = property.Value;
474+
}
475+
}
476+
}
477+
478+
throw exception;
457479
}
458480

459481
if (response is JsonRpcResponse success)

tests/ModelContextProtocol.Tests/McpProtocolExceptionDataTests.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using ModelContextProtocol.Protocol;
44
using ModelContextProtocol.Server;
55
using ModelContextProtocol.Tests.Utils;
6+
using System.Text.Json;
67

78
namespace ModelContextProtocol.Tests;
89

@@ -72,6 +73,28 @@ public async Task Exception_With_Serializable_Data_Propagates_To_Client()
7273

7374
Assert.Equal("Request failed (remote): Resource not found", exception.Message);
7475
Assert.Equal((McpErrorCode)(-32002), exception.ErrorCode);
76+
77+
// Verify the data was propagated to the exception
78+
// The Data collection should contain the expected keys
79+
var hasUri = false;
80+
var hasCode = false;
81+
foreach (System.Collections.DictionaryEntry entry in exception.Data)
82+
{
83+
if (entry.Key is string key)
84+
{
85+
if (key == "uri") hasUri = true;
86+
if (key == "code") hasCode = true;
87+
}
88+
}
89+
Assert.True(hasUri, "Exception.Data should contain 'uri' key");
90+
Assert.True(hasCode, "Exception.Data should contain 'code' key");
91+
92+
// Verify the values (they should be JsonElements)
93+
var uriValue = Assert.IsType<JsonElement>(exception.Data["uri"]);
94+
Assert.Equal("file:///path/to/resource", uriValue.GetString());
95+
96+
var codeValue = Assert.IsType<JsonElement>(exception.Data["code"]);
97+
Assert.Equal(404, codeValue.GetInt32());
7598
}
7699

77100
[Fact]
@@ -87,6 +110,23 @@ public async Task Exception_With_NonSerializable_Data_Still_Propagates_Error_To_
87110

88111
Assert.Equal("Request failed (remote): Resource not found", exception.Message);
89112
Assert.Equal((McpErrorCode)(-32002), exception.ErrorCode);
113+
114+
// Verify that only the serializable data was propagated (non-serializable was filtered out)
115+
var hasUri = false;
116+
var hasNonSerializable = false;
117+
foreach (System.Collections.DictionaryEntry entry in exception.Data)
118+
{
119+
if (entry.Key is string key)
120+
{
121+
if (key == "uri") hasUri = true;
122+
if (key == "nonSerializable") hasNonSerializable = true;
123+
}
124+
}
125+
Assert.True(hasUri, "Exception.Data should contain 'uri' key");
126+
Assert.False(hasNonSerializable, "Exception.Data should not contain 'nonSerializable' key");
127+
128+
var uriValue = Assert.IsType<JsonElement>(exception.Data["uri"]);
129+
Assert.Equal("file:///path/to/resource", uriValue.GetString());
90130
}
91131

92132
[Fact]
@@ -100,6 +140,10 @@ public async Task Exception_With_Only_NonSerializable_Data_Still_Propagates_Erro
100140

101141
Assert.Equal("Request failed (remote): Resource not found", exception.Message);
102142
Assert.Equal((McpErrorCode)(-32002), exception.ErrorCode);
143+
144+
// When all data is non-serializable, the Data collection should be empty
145+
// (the server's ConvertExceptionData returns null when no serializable data exists)
146+
Assert.Empty(exception.Data);
103147
}
104148

105149
/// <summary>

0 commit comments

Comments
 (0)