|
6 | 6 | using System.Reflection; |
7 | 7 | using System.Text.Json; |
8 | 8 | using System.Text.Json.Nodes; |
| 9 | +using System.Text.Json.Serialization.Metadata; |
9 | 10 | using System.Text.RegularExpressions; |
10 | 11 |
|
11 | 12 | namespace ModelContextProtocol.Server; |
@@ -145,14 +146,9 @@ options.OpenWorld is not null || |
145 | 146 | } |
146 | 147 |
|
147 | 148 | // Populate Meta from options and/or McpMetaAttribute instances if a MethodInfo is available |
148 | | - if (function.UnderlyingMethod is not null) |
149 | | - { |
150 | | - tool.Meta = CreateMetaFromAttributes(function.UnderlyingMethod, options.Meta, options.SerializerOptions); |
151 | | - } |
152 | | - else if (options.Meta is not null) |
153 | | - { |
154 | | - tool.Meta = options.Meta; |
155 | | - } |
| 149 | + tool.Meta = function.UnderlyingMethod is not null ? |
| 150 | + CreateMetaFromAttributes(function.UnderlyingMethod, options.Meta, options.SerializerOptions) : |
| 151 | + options.Meta; |
156 | 152 | } |
157 | 153 |
|
158 | 154 | return new AIFunctionMcpServerTool(function, tool, options?.Services, structuredOutputRequiresWrapping, options?.Metadata ?? []); |
@@ -361,25 +357,35 @@ internal static IReadOnlyList<object> CreateMetadata(MethodInfo method) |
361 | 357 | return metadata.AsReadOnly(); |
362 | 358 | } |
363 | 359 |
|
364 | | - /// <summary>Creates a Meta JsonObject from McpMetaAttribute instances on the specified method.</summary> |
365 | | - /// <param name="method">The method to extract McpMetaAttribute instances from.</param> |
366 | | - /// <param name="seedMeta">Optional JsonObject to seed the Meta with. Properties from this object take precedence over attributes.</param> |
367 | | - /// <param name="serializerOptions">Optional JsonSerializerOptions to use for serialization. Defaults to McpJsonUtilities.DefaultOptions if not provided.</param> |
368 | | - /// <returns>A JsonObject with metadata, or null if no metadata is present.</returns> |
369 | | - internal static JsonObject? CreateMetaFromAttributes(MethodInfo method, JsonObject? seedMeta = null, JsonSerializerOptions? serializerOptions = null) |
| 360 | + /// <summary>Creates a Meta <see cref="JsonObject"/> from <see cref="McpMetaAttribute"/> instances on the specified method.</summary> |
| 361 | + /// <param name="method">The method to extract <see cref="McpMetaAttribute"/> instances from.</param> |
| 362 | + /// <param name="meta">Optional <see cref="JsonObject"/> to seed the Meta with. Properties from this object take precedence over attributes.</param> |
| 363 | + /// <param name="serializerOptions">Optional <see cref="JsonSerializerOptions"/> to use for serialization. Defaults to <see cref="McpJsonUtilities.DefaultOptions"/> if not provided.</param> |
| 364 | + /// <returns>A <see cref="JsonObject"/> with metadata, or null if no metadata is present.</returns> |
| 365 | + internal static JsonObject? CreateMetaFromAttributes(MethodInfo method, JsonObject? meta = null, JsonSerializerOptions? serializerOptions = null) |
370 | 366 | { |
371 | | - // Get all McpMetaAttribute instances from the method |
| 367 | + // Get all McpMetaAttribute instances from the method. |
372 | 368 | var metaAttributes = method.GetCustomAttributes<McpMetaAttribute>(); |
373 | 369 |
|
374 | | - JsonObject? meta = seedMeta; |
375 | | - JsonSerializerOptions options = serializerOptions ?? McpJsonUtilities.DefaultOptions; |
376 | 370 | foreach (var attr in metaAttributes) |
377 | 371 | { |
378 | | - meta ??= new JsonObject(); |
379 | | - // Only add the attribute property if it doesn't already exist in the seed |
| 372 | + meta ??= []; |
380 | 373 | if (!meta.ContainsKey(attr.Name)) |
381 | 374 | { |
382 | | - meta[attr.Name] = JsonSerializer.SerializeToNode(attr.Value, options.GetTypeInfo(typeof(object))); |
| 375 | + JsonTypeInfo? valueTypeInfo = null; |
| 376 | + if (attr.Value?.GetType() is { } valueType) |
| 377 | + { |
| 378 | + if (serializerOptions?.TryGetTypeInfo(valueType, out valueTypeInfo) is not true && |
| 379 | + McpJsonUtilities.DefaultOptions.TryGetTypeInfo(valueType, out valueTypeInfo) is not true) |
| 380 | + { |
| 381 | + // Throw using GetTypeInfo in order to get a good exception message. |
| 382 | + (serializerOptions ?? McpJsonUtilities.DefaultOptions).GetTypeInfo(valueType); |
| 383 | + } |
| 384 | + |
| 385 | + Debug.Assert(valueTypeInfo is not null, "GetTypeInfo should have thrown an exception"); |
| 386 | + } |
| 387 | + |
| 388 | + meta[attr.Name] = valueTypeInfo is not null ? JsonSerializer.SerializeToNode(attr.Value, valueTypeInfo) : null; |
383 | 389 | } |
384 | 390 | } |
385 | 391 |
|
|
0 commit comments