Skip to content

Commit 389fb3d

Browse files
committed
Fix JSON reference issues
1 parent c7183f6 commit 389fb3d

File tree

3 files changed

+78
-38
lines changed

3 files changed

+78
-38
lines changed

src/ModelContextProtocol/Protocol/Auth/AuthorizationService.cs

Lines changed: 70 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Text;
55
using System.Text.Json;
66
using ModelContextProtocol.Utils;
7+
using ModelContextProtocol.Utils.Json;
78

89
namespace ModelContextProtocol.Protocol.Auth;
910

@@ -65,12 +66,12 @@ internal class AuthorizationService
6566
using var metadataResponse = await s_httpClient.GetAsync(resourceMetadataUrl);
6667
metadataResponse.EnsureSuccessStatusCode();
6768

68-
return await JsonSerializer.DeserializeAsync<ResourceMetadata>(
69-
await metadataResponse.Content.ReadAsStreamAsync(),
70-
new JsonSerializerOptions
71-
{
72-
PropertyNameCaseInsensitive = true
73-
});
69+
var contentStream = await metadataResponse.Content.ReadAsStreamAsync();
70+
71+
// Read as string first, then deserialize using source-generated serializer
72+
using var reader = new StreamReader(contentStream);
73+
var json = await reader.ReadToEndAsync();
74+
return JsonSerializer.Deserialize(json, McpJsonUtilities.JsonContext.Default.ResourceMetadata);
7475
}
7576
catch (Exception)
7677
{
@@ -102,12 +103,19 @@ public static async Task<AuthorizationServerMetadata> DiscoverAuthorizationServe
102103
using var openIdResponse = await s_httpClient.GetAsync(openIdConfigUrl);
103104
if (openIdResponse.IsSuccessStatusCode)
104105
{
105-
return await JsonSerializer.DeserializeAsync<AuthorizationServerMetadata>(
106-
await openIdResponse.Content.ReadAsStreamAsync(),
107-
new JsonSerializerOptions
108-
{
109-
PropertyNameCaseInsensitive = true
110-
}) ?? throw new InvalidOperationException("Failed to parse authorization server metadata");
106+
var contentStream = await openIdResponse.Content.ReadAsStreamAsync();
107+
108+
// Use source-generated serialization instead of dynamic deserialization
109+
using var reader = new StreamReader(contentStream);
110+
var json = await reader.ReadToEndAsync();
111+
var result = JsonSerializer.Deserialize(json, McpJsonUtilities.JsonContext.Default.AuthorizationServerMetadata);
112+
113+
if (result == null)
114+
{
115+
throw new InvalidOperationException("Failed to parse authorization server metadata");
116+
}
117+
118+
return result;
111119
}
112120
}
113121
catch (Exception ex) when (ex is not InvalidOperationException)
@@ -122,12 +130,19 @@ await openIdResponse.Content.ReadAsStreamAsync(),
122130
using var oauthResponse = await s_httpClient.GetAsync(oauthConfigUrl);
123131
if (oauthResponse.IsSuccessStatusCode)
124132
{
125-
return await JsonSerializer.DeserializeAsync<AuthorizationServerMetadata>(
126-
await oauthResponse.Content.ReadAsStreamAsync(),
127-
new JsonSerializerOptions
128-
{
129-
PropertyNameCaseInsensitive = true
130-
}) ?? throw new InvalidOperationException("Failed to parse authorization server metadata");
133+
var contentStream = await oauthResponse.Content.ReadAsStreamAsync();
134+
135+
// Use source-generated serialization instead of dynamic deserialization
136+
using var reader = new StreamReader(contentStream);
137+
var json = await reader.ReadToEndAsync();
138+
var result = JsonSerializer.Deserialize(json, McpJsonUtilities.JsonContext.Default.AuthorizationServerMetadata);
139+
140+
if (result == null)
141+
{
142+
throw new InvalidOperationException("Failed to parse authorization server metadata");
143+
}
144+
145+
return result;
131146
}
132147
}
133148
catch (Exception ex) when (ex is not InvalidOperationException)
@@ -160,19 +175,25 @@ public static async Task<ClientRegistrationResponse> RegisterClientAsync(
160175
}
161176

162177
var content = new StringContent(
163-
JsonSerializer.Serialize(clientMetadata),
178+
JsonSerializer.Serialize(clientMetadata, McpJsonUtilities.JsonContext.Default.ClientMetadata),
164179
Encoding.UTF8,
165180
"application/json");
166181

167182
using var response = await s_httpClient.PostAsync(metadata.RegistrationEndpoint, content);
168183
response.EnsureSuccessStatusCode();
169184

170-
return await JsonSerializer.DeserializeAsync<ClientRegistrationResponse>(
171-
await response.Content.ReadAsStreamAsync(),
172-
new JsonSerializerOptions
173-
{
174-
PropertyNameCaseInsensitive = true
175-
}) ?? throw new InvalidOperationException("Failed to parse client registration response");
185+
// Use source-generated serialization instead of dynamic deserialization
186+
var contentStream = await response.Content.ReadAsStreamAsync();
187+
using var reader = new StreamReader(contentStream);
188+
var json = await reader.ReadToEndAsync();
189+
var result = JsonSerializer.Deserialize(json, McpJsonUtilities.JsonContext.Default.ClientRegistrationResponse);
190+
191+
if (result == null)
192+
{
193+
throw new InvalidOperationException("Failed to parse client registration response");
194+
}
195+
196+
return result;
176197
}
177198

178199
/// <summary>
@@ -293,12 +314,18 @@ public static async Task<TokenResponse> ExchangeCodeForTokensAsync(
293314
using var response = await s_httpClient.SendAsync(request);
294315
response.EnsureSuccessStatusCode();
295316

296-
return await JsonSerializer.DeserializeAsync<TokenResponse>(
297-
await response.Content.ReadAsStreamAsync(),
298-
new JsonSerializerOptions
299-
{
300-
PropertyNameCaseInsensitive = true
301-
}) ?? throw new InvalidOperationException("Failed to parse token response");
317+
// Use source-generated serialization instead of dynamic deserialization
318+
var contentStream = await response.Content.ReadAsStreamAsync();
319+
using var reader = new StreamReader(contentStream);
320+
var json = await reader.ReadToEndAsync();
321+
var result = JsonSerializer.Deserialize(json, McpJsonUtilities.JsonContext.Default.TokenResponse);
322+
323+
if (result == null)
324+
{
325+
throw new InvalidOperationException("Failed to parse token response");
326+
}
327+
328+
return result;
302329
}
303330

304331
/// <summary>
@@ -341,12 +368,18 @@ public static async Task<TokenResponse> RefreshTokenAsync(
341368
using var response = await s_httpClient.SendAsync(request);
342369
response.EnsureSuccessStatusCode();
343370

344-
return await JsonSerializer.DeserializeAsync<TokenResponse>(
345-
await response.Content.ReadAsStreamAsync(),
346-
new JsonSerializerOptions
347-
{
348-
PropertyNameCaseInsensitive = true
349-
}) ?? throw new InvalidOperationException("Failed to parse token response");
371+
// Use source-generated serialization instead of dynamic deserialization
372+
var contentStream = await response.Content.ReadAsStreamAsync();
373+
using var reader = new StreamReader(contentStream);
374+
var json = await reader.ReadToEndAsync();
375+
var result = JsonSerializer.Deserialize(json, McpJsonUtilities.JsonContext.Default.TokenResponse);
376+
377+
if (result == null)
378+
{
379+
throw new InvalidOperationException("Failed to parse token response");
380+
}
381+
382+
return result;
350383
}
351384

352385
private static Dictionary<string, string> ParseAuthHeaderParameters(string parameters)

src/ModelContextProtocol/Protocol/Transport/SseClientTransport.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace ModelContextProtocol.Protocol.Transport;
1616
/// </para>
1717
/// <para>
1818
/// The SSE transport can handle OAuth 2.0 authorization flows when connecting to servers that require authentication.
19-
/// You can provide an <see cref="AuthorizeCallback"/> in the transport options to handle the user authentication part
19+
/// You can provide an <see cref="SseClientTransportOptions.AuthorizeCallback"/> in the transport options to handle the user authentication part
2020
/// of the OAuth flow.
2121
/// </para>
2222
/// </remarks>

src/ModelContextProtocol/Utils/Json/McpJsonUtilities.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ internal static bool IsValidMcpToolSchema(JsonElement element)
122122
[JsonSerializable(typeof(SubscribeRequestParams))]
123123
[JsonSerializable(typeof(UnsubscribeRequestParams))]
124124
[JsonSerializable(typeof(IReadOnlyDictionary<string, object>))]
125+
126+
// Authorization-related types
127+
[JsonSerializable(typeof(Protocol.Auth.ResourceMetadata))]
128+
[JsonSerializable(typeof(Protocol.Auth.AuthorizationServerMetadata))]
129+
[JsonSerializable(typeof(Protocol.Auth.ClientMetadata))]
130+
[JsonSerializable(typeof(Protocol.Auth.ClientRegistrationResponse))]
131+
[JsonSerializable(typeof(Protocol.Auth.TokenResponse))]
125132

126133
[ExcludeFromCodeCoverage]
127134
internal sealed partial class JsonContext : JsonSerializerContext;

0 commit comments

Comments
 (0)