diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionMetadata.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionMetadata.cs
index 528212c4b2b..d6e5279d589 100644
--- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionMetadata.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionMetadata.cs
@@ -17,19 +17,22 @@ namespace Microsoft.Extensions.AI;
 public sealed class AIFunctionMetadata
 {
     /// The name of the function.
-    private string _name = string.Empty;
+    private readonly string _name = string.Empty;
 
     /// The description of the function.
-    private string _description = string.Empty;
+    private readonly string _description = string.Empty;
+
+    /// The JSON schema describing the function and its input parameters.
+    private readonly JsonElement _schema = AIJsonUtilities.DefaultJsonSchema;
 
     /// The function's parameters.
-    private IReadOnlyList _parameters = [];
+    private readonly IReadOnlyList _parameters = [];
 
     /// The function's return parameter.
-    private AIFunctionReturnParameterMetadata _returnParameter = AIFunctionReturnParameterMetadata.Empty;
+    private readonly AIFunctionReturnParameterMetadata _returnParameter = AIFunctionReturnParameterMetadata.Empty;
 
     /// Optional additional properties in addition to the named properties already available on this class.
-    private IReadOnlyDictionary _additionalProperties = EmptyReadOnlyDictionary.Instance;
+    private readonly IReadOnlyDictionary _additionalProperties = EmptyReadOnlyDictionary.Instance;
 
     ///  indexed by name, lazily initialized.
     private Dictionary? _parametersByName;
@@ -55,6 +58,7 @@ public AIFunctionMetadata(AIFunctionMetadata metadata)
         Parameters = metadata.Parameters;
         ReturnParameter = metadata.ReturnParameter;
         AdditionalProperties = metadata.AdditionalProperties;
+        Schema = metadata.Schema;
     }
 
     /// Gets the name of the function.
@@ -100,6 +104,43 @@ public AIFunctionReturnParameterMetadata ReturnParameter
         init => _returnParameter = Throw.IfNull(value);
     }
 
+    /// Gets a JSON Schema describing the function and its input parameters.
+    /// 
+    /// 
+    /// When specified, declares a self-contained JSON schema document that describes the function and its input parameters.
+    /// A simple example of a JSON schema for a function that adds two numbers together is shown below:
+    /// 
+    /// 
+    /// {
+    ///   "title" : "addNumbers",
+    ///   "description": "A simple function that adds two numbers together.",
+    ///   "type": "object",
+    ///   "properties": {
+    ///     "a" : { "type": "number" },
+    ///     "b" : { "type": "number", "default": 1 }
+    ///   }, 
+    ///   "required" : ["a"]
+    /// }
+    /// 
+    /// 
+    /// The metadata present in the schema document plays an important role in guiding AI function invocation.
+    /// Functions should incorporate as much detail as possible. The arity of the "properties" keyword should
+    /// also match the length of the  list.
+    /// 
+    /// 
+    /// When no schema is specified, consuming chat clients should assume the "{}" or "true" schema, indicating that any JSON input is admissible.
+    /// 
+    /// 
+    public JsonElement Schema
+    {
+        get => _schema;
+        init
+        {
+            AIJsonUtilities.ValidateSchemaDocument(value);
+            _schema = value;
+        }
+    }
+
     /// Gets any additional properties associated with the function.
     public IReadOnlyDictionary AdditionalProperties
     {
diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionParameterMetadata.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionParameterMetadata.cs
index b2e77f619db..372083fd799 100644
--- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionParameterMetadata.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionParameterMetadata.cs
@@ -11,7 +11,7 @@ namespace Microsoft.Extensions.AI;
 /// 
 public sealed class AIFunctionParameterMetadata
 {
-    private string _name;
+    private readonly string _name;
 
     /// Initializes a new instance of the  class for a parameter with the specified name.
     /// The name of the parameter.
@@ -37,7 +37,6 @@ public AIFunctionParameterMetadata(AIFunctionParameterMetadata metadata)
         DefaultValue = metadata.DefaultValue;
         IsRequired = metadata.IsRequired;
         ParameterType = metadata.ParameterType;
-        Schema = metadata.Schema;
     }
 
     /// Gets the name of the parameter.
@@ -61,7 +60,4 @@ public string Name
 
     /// Gets the .NET type of the parameter.
     public Type? ParameterType { get; init; }
-
-    /// Gets a JSON Schema describing the parameter's type.
-    public object? Schema { get; init; }
 }
diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionReturnParameterMetadata.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionReturnParameterMetadata.cs
index e96e67d4806..9ca5b3b6e49 100644
--- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionReturnParameterMetadata.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionReturnParameterMetadata.cs
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Text.Json;
 using Microsoft.Shared.Diagnostics;
 
 namespace Microsoft.Extensions.AI;
@@ -14,6 +15,9 @@ public sealed class AIFunctionReturnParameterMetadata
     /// Gets an empty return parameter metadata instance.
     public static AIFunctionReturnParameterMetadata Empty { get; } = new();
 
+    /// The JSON schema describing the function and its input parameters.
+    private readonly JsonElement _schema = AIJsonUtilities.DefaultJsonSchema;
+
     /// Initializes a new instance of the  class.
     public AIFunctionReturnParameterMetadata()
     {
@@ -34,5 +38,13 @@ public AIFunctionReturnParameterMetadata(AIFunctionReturnParameterMetadata metad
     public Type? ParameterType { get; init; }
 
     /// Gets a JSON Schema describing the type of the return parameter.
-    public object? Schema { get; init; }
+    public JsonElement Schema
+    {
+        get => _schema;
+        init
+        {
+            AIJsonUtilities.ValidateSchemaDocument(value);
+            _schema = value;
+        }
+    }
 }
diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Schema.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Schema.cs
index 1af2e5a8c92..ee2e497638a 100644
--- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Schema.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Schema.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
-using System.Collections.Concurrent;
+using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
@@ -25,100 +25,90 @@ namespace Microsoft.Extensions.AI;
 /// Provides a collection of utility methods for marshalling JSON data.
 public static partial class AIJsonUtilities
 {
+    private const string SchemaPropertyName = "$schema";
+    private const string TitlePropertyName = "title";
+    private const string DescriptionPropertyName = "description";
+    private const string NotPropertyName = "not";
+    private const string TypePropertyName = "type";
+    private const string PatternPropertyName = "pattern";
+    private const string EnumPropertyName = "enum";
+    private const string PropertiesPropertyName = "properties";
+    private const string RequiredPropertyName = "required";
+    private const string AdditionalPropertiesPropertyName = "additionalProperties";
+    private const string DefaultPropertyName = "default";
+    private const string RefPropertyName = "$ref";
+
     /// The uri used when populating the $schema keyword in inferred schemas.
     private const string SchemaKeywordUri = "https://json-schema.org/draft/2020-12/schema";
 
-    /// Soft limit for how many items should be stored in the dictionaries in .
-    private const int CacheSoftLimit = 4096;
-
-    /// Caches of generated schemas for each  that's employed.
-    private static readonly ConditionalWeakTable> _schemaCaches = new();
-
-    /// Gets a JSON schema accepting all values.
-    private static readonly JsonElement _trueJsonSchema = ParseJsonElement("true"u8);
-
-    /// Gets a JSON schema only accepting null values.
-    private static readonly JsonElement _nullJsonSchema = ParseJsonElement("""{"type":"null"}"""u8);
-
     // List of keywords used by JsonSchemaExporter but explicitly disallowed by some AI vendors.
     // cf. https://platform.openai.com/docs/guides/structured-outputs#some-type-specific-keywords-are-not-yet-supported
     private static readonly string[] _schemaKeywordsDisallowedByAIVendors = ["minLength", "maxLength", "pattern", "format"];
 
     /// 
-    /// Determines a JSON schema for the provided parameter metadata.
+    /// Determines a JSON schema for the provided AI function parameter metadata.
     /// 
-    /// The parameter metadata from which to infer the schema.
-    /// The containing function metadata.
+    /// The title keyword used by the method schema.
+    /// The description keyword used by the method schema.
+    /// The AI function parameter metadata.
     /// The options used to extract the schema from the specified type.
     /// The options controlling schema inference.
     /// A JSON schema document encoded as a .
-    public static JsonElement ResolveParameterJsonSchema(
-        AIFunctionParameterMetadata parameterMetadata,
-        AIFunctionMetadata functionMetadata,
+    public static JsonElement CreateFunctionJsonSchema(
+        string? title = null,
+        string? description = null,
+        IReadOnlyList? parameters = null,
         JsonSerializerOptions? serializerOptions = null,
         AIJsonSchemaCreateOptions? inferenceOptions = null)
     {
-        _ = Throw.IfNull(parameterMetadata);
-        _ = Throw.IfNull(functionMetadata);
+        serializerOptions ??= DefaultOptions;
+        inferenceOptions ??= AIJsonSchemaCreateOptions.Default;
 
-        serializerOptions ??= functionMetadata.JsonSerializerOptions ?? DefaultOptions;
+        JsonObject parameterSchemas = new();
+        JsonArray? requiredProperties = null;
+        foreach (AIFunctionParameterMetadata parameter in parameters ?? [])
+        {
+            JsonNode parameterSchema = CreateJsonSchemaCore(
+                parameter.ParameterType,
+                parameter.Name,
+                parameter.Description,
+                parameter.HasDefaultValue,
+                parameter.DefaultValue,
+                serializerOptions,
+                inferenceOptions);
+
+            parameterSchemas.Add(parameter.Name, parameterSchema);
+            if (parameter.IsRequired)
+            {
+                (requiredProperties ??= []).Add((JsonNode)parameter.Name);
+            }
+        }
 
-        if (ReferenceEquals(serializerOptions, functionMetadata.JsonSerializerOptions) &&
-            parameterMetadata.Schema is JsonElement schema)
+        JsonObject schema = new();
+        if (inferenceOptions.IncludeSchemaKeyword)
         {
-            // If the resolved options matches that of the function metadata,
-            // we can just return the precomputed JSON schema value.
-            return schema;
+            schema[SchemaPropertyName] = SchemaKeywordUri;
         }
 
-        return CreateParameterJsonSchema(
-            parameterMetadata.ParameterType,
-            parameterMetadata.Name,
-            description: parameterMetadata.Description,
-            hasDefaultValue: parameterMetadata.HasDefaultValue,
-            defaultValue: parameterMetadata.DefaultValue,
-            serializerOptions,
-            inferenceOptions);
-    }
+        if (!string.IsNullOrWhiteSpace(title))
+        {
+            schema[TitlePropertyName] = title;
+        }
 
-    /// 
-    /// Creates a JSON schema for the provided parameter metadata.
-    /// 
-    /// The type of the parameter.
-    /// The name of the parameter.
-    /// The description of the parameter.
-    ///  if the parameter is optional; otherwise, .
-    /// The default value of the optional parameter, if applicable.
-    /// The options used to extract the schema from the specified type.
-    /// The options controlling schema inference.
-    /// A JSON schema document encoded as a .
-    /// 
-    /// Uses a cache keyed on the  to store schema result,
-    /// unless a  delegate has been specified.
-    /// 
-    public static JsonElement CreateParameterJsonSchema(
-        Type? type,
-        string parameterName,
-        string? description = null,
-        bool hasDefaultValue = false,
-        object? defaultValue = null,
-        JsonSerializerOptions? serializerOptions = null,
-        AIJsonSchemaCreateOptions? inferenceOptions = null)
-    {
-        _ = Throw.IfNull(parameterName);
+        if (!string.IsNullOrWhiteSpace(description))
+        {
+            schema[DescriptionPropertyName] = description;
+        }
 
-        serializerOptions ??= DefaultOptions;
-        inferenceOptions ??= AIJsonSchemaCreateOptions.Default;
+        schema[TypePropertyName] = "object"; // Method schemas always hardcode the type as "object".
+        schema[PropertiesPropertyName] = parameterSchemas;
 
-        SchemaGenerationKey key = new(
-            type,
-            parameterName,
-            description,
-            hasDefaultValue,
-            defaultValue,
-            inferenceOptions);
+        if (requiredProperties is not null)
+        {
+            schema[RequiredPropertyName] = requiredProperties;
+        }
 
-        return GetJsonSchemaCached(serializerOptions, key);
+        return JsonSerializer.SerializeToElement(schema, JsonContext.Default.JsonNode);
     }
 
     /// Creates a JSON schema for the specified type.
@@ -129,10 +119,6 @@ public static JsonElement CreateParameterJsonSchema(
     /// The options used to extract the schema from the specified type.
     /// The options controlling schema inference.
     /// A  representing the schema.
-    /// 
-    /// Uses a cache keyed on the  to store schema result,
-    /// unless a  delegate has been specified.
-    /// 
     public static JsonElement CreateJsonSchema(
         Type? type,
         string? description = null,
@@ -143,36 +129,20 @@ public static JsonElement CreateJsonSchema(
     {
         serializerOptions ??= DefaultOptions;
         inferenceOptions ??= AIJsonSchemaCreateOptions.Default;
-
-        SchemaGenerationKey key = new(
-            type,
-            parameterName: null,
-            description,
-            hasDefaultValue,
-            defaultValue,
-            inferenceOptions);
-
-        return GetJsonSchemaCached(serializerOptions, key);
+        JsonNode schema = CreateJsonSchemaCore(type, parameterName: null, description, hasDefaultValue, defaultValue, serializerOptions, inferenceOptions);
+        return JsonSerializer.SerializeToElement(schema, JsonContext.Default.JsonNode);
     }
 
-    private static JsonElement GetJsonSchemaCached(JsonSerializerOptions options, SchemaGenerationKey key)
-    {
-        options.MakeReadOnly();
-        ConcurrentDictionary cache = _schemaCaches.GetOrCreateValue(options);
+    /// Gets the default JSON schema to be used by types or functions.
+    internal static JsonElement DefaultJsonSchema { get; } = ParseJsonElement("{}"u8);
 
-        if (key.TransformSchemaNode is not null || cache.Count >= CacheSoftLimit)
+    /// Validates the provided JSON schema document.
+    internal static void ValidateSchemaDocument(JsonElement document, [CallerArgumentExpression("document")] string? paramName = null)
+    {
+        if (document.ValueKind is not JsonValueKind.Object or JsonValueKind.False or JsonValueKind.True)
         {
-            return GetJsonSchemaCore(options, key);
+            Throw.ArgumentException(paramName ?? "schema", "The schema document must be an object or a boolean value.");
         }
-
-        return cache.GetOrAdd(
-            key: key,
-#if NET
-            valueFactory: static (key, options) => GetJsonSchemaCore(options, key),
-            factoryArgument: options);
-#else
-            valueFactory: key => GetJsonSchemaCore(options, key));
-#endif
     }
 
 #if !NET9_0_OR_GREATER
@@ -180,44 +150,48 @@ private static JsonElement GetJsonSchemaCached(JsonSerializerOptions options, Sc
         Justification = "Pre STJ-9 schema extraction can fail with a runtime exception if certain reflection metadata have been trimmed. " +
                         "The exception message will guide users to turn off 'IlcTrimMetadata' which resolves all issues.")]
 #endif
-    private static JsonElement GetJsonSchemaCore(JsonSerializerOptions options, SchemaGenerationKey key)
+    private static JsonNode CreateJsonSchemaCore(
+        Type? type,
+        string? parameterName,
+        string? description,
+        bool hasDefaultValue,
+        object? defaultValue,
+        JsonSerializerOptions serializerOptions,
+        AIJsonSchemaCreateOptions inferenceOptions)
     {
-        _ = Throw.IfNull(options);
-        options.MakeReadOnly();
+        serializerOptions.MakeReadOnly();
 
-        if (key.Type is null)
+        if (type is null)
         {
             // For parameters without a type generate a rudimentary schema with available metadata.
 
             JsonObject? schemaObj = null;
 
-            if (key.IncludeSchemaKeyword)
+            if (inferenceOptions.IncludeSchemaKeyword)
             {
-                (schemaObj = [])["$schema"] = SchemaKeywordUri;
+                (schemaObj = [])[SchemaPropertyName] = SchemaKeywordUri;
             }
 
-            if (key.Description is not null)
+            if (description is not null)
             {
-                (schemaObj ??= [])["description"] = key.Description;
+                (schemaObj ??= [])[DescriptionPropertyName] = description;
             }
 
-            if (key.HasDefaultValue)
+            if (hasDefaultValue)
             {
-                JsonNode? defaultValueNode = key.DefaultValue is { } defaultValue
-                    ? JsonSerializer.Serialize(defaultValue, options.GetTypeInfo(defaultValue.GetType()))
+                JsonNode? defaultValueNode = defaultValue is not null
+                    ? JsonSerializer.Serialize(defaultValue, serializerOptions.GetTypeInfo(defaultValue.GetType()))
                     : null;
 
-                (schemaObj ??= [])["default"] = defaultValueNode;
+                (schemaObj ??= [])[DefaultPropertyName] = defaultValueNode;
             }
 
-            return schemaObj is null
-                ? _trueJsonSchema
-                : JsonSerializer.SerializeToElement(schemaObj, options.GetTypeInfo(typeof(JsonNode)));
+            return schemaObj ?? (JsonNode)true;
         }
 
-        if (key.Type == typeof(void))
+        if (type == typeof(void))
         {
-            return _nullJsonSchema;
+            return new JsonObject { [TypePropertyName] = null };
         }
 
         JsonSchemaExporterOptions exporterOptions = new()
@@ -226,23 +200,10 @@ private static JsonElement GetJsonSchemaCore(JsonSerializerOptions options, Sche
             TransformSchemaNode = TransformSchemaNode,
         };
 
-        JsonNode node = options.GetJsonSchemaAsNode(key.Type, exporterOptions);
-        return JsonSerializer.SerializeToElement(node, DefaultOptions.GetTypeInfo(typeof(JsonNode)));
+        return serializerOptions.GetJsonSchemaAsNode(type, exporterOptions);
 
         JsonNode TransformSchemaNode(JsonSchemaExporterContext schemaExporterContext, JsonNode schema)
         {
-            const string SchemaPropertyName = "$schema";
-            const string DescriptionPropertyName = "description";
-            const string NotPropertyName = "not";
-            const string TypePropertyName = "type";
-            const string PatternPropertyName = "pattern";
-            const string EnumPropertyName = "enum";
-            const string PropertiesPropertyName = "properties";
-            const string RequiredPropertyName = "required";
-            const string AdditionalPropertiesPropertyName = "additionalProperties";
-            const string DefaultPropertyName = "default";
-            const string RefPropertyName = "$ref";
-
             AIJsonSchemaCreateContext ctx = new(schemaExporterContext);
 
             if (ctx.GetCustomAttribute() is { } attr)
@@ -255,26 +216,26 @@ JsonNode TransformSchemaNode(JsonSchemaExporterContext schemaExporterContext, Js
                 // The resulting schema might be a $ref using a pointer to a different location in the document.
                 // As JSON pointer doesn't support relative paths, parameter schemas need to fix up such paths
                 // to accommodate the fact that they're being nested inside of a higher-level schema.
-                if (key.ParameterName is not null && objSchema.TryGetPropertyValue(RefPropertyName, out JsonNode? paramName))
+                if (parameterName is not null && objSchema.TryGetPropertyValue(RefPropertyName, out JsonNode? paramName))
                 {
                     // Fix up any $ref URIs to match the path from the root document.
                     string refUri = paramName!.GetValue();
                     Debug.Assert(refUri is "#" || refUri.StartsWith("#/", StringComparison.Ordinal), $"Expected {nameof(refUri)} to be either # or start with #/, got {refUri}");
                     refUri = refUri == "#"
-                        ? $"#/{PropertiesPropertyName}/{key.ParameterName}"
-                        : $"#/{PropertiesPropertyName}/{key.ParameterName}/{refUri.AsMemory("#/".Length)}";
+                        ? $"#/{PropertiesPropertyName}/{parameterName}"
+                        : $"#/{PropertiesPropertyName}/{parameterName}/{refUri.AsMemory("#/".Length)}";
 
                     objSchema[RefPropertyName] = (JsonNode)refUri;
                 }
 
                 // Include the type keyword in enum types
-                if (key.IncludeTypeInEnumSchemas && ctx.TypeInfo.Type.IsEnum && objSchema.ContainsKey(EnumPropertyName) && !objSchema.ContainsKey(TypePropertyName))
+                if (inferenceOptions.IncludeTypeInEnumSchemas && ctx.TypeInfo.Type.IsEnum && objSchema.ContainsKey(EnumPropertyName) && !objSchema.ContainsKey(TypePropertyName))
                 {
                     objSchema.InsertAtStart(TypePropertyName, "string");
                 }
 
                 // Disallow additional properties in object schemas
-                if (key.DisallowAdditionalProperties &&
+                if (inferenceOptions.DisallowAdditionalProperties &&
                     objSchema.ContainsKey(PropertiesPropertyName) &&
                     !objSchema.ContainsKey(AdditionalPropertiesPropertyName))
                 {
@@ -282,7 +243,7 @@ JsonNode TransformSchemaNode(JsonSchemaExporterContext schemaExporterContext, Js
                 }
 
                 // Mark all properties as required
-                if (key.RequireAllProperties &&
+                if (inferenceOptions.RequireAllProperties &&
                     objSchema.TryGetPropertyValue(PropertiesPropertyName, out JsonNode? properties) &&
                     properties is JsonObject propertiesObj)
                 {
@@ -318,30 +279,29 @@ JsonNode TransformSchemaNode(JsonSchemaExporterContext schemaExporterContext, Js
             {
                 // We are at the root-level schema node, update/append parameter-specific metadata
 
-                if (!string.IsNullOrWhiteSpace(key.Description))
+                if (!string.IsNullOrWhiteSpace(description))
                 {
                     JsonObject obj = ConvertSchemaToObject(ref schema);
-                    JsonNode descriptionNode = (JsonNode)key.Description!;
                     int index = obj.IndexOf(DescriptionPropertyName);
                     if (index < 0)
                     {
                         // If there's no description property, insert it at the beginning of the doc.
-                        obj.InsertAtStart(DescriptionPropertyName, (JsonNode)key.Description!);
+                        obj.InsertAtStart(DescriptionPropertyName, (JsonNode)description!);
                     }
                     else
                     {
                         // If there is a description property, just update it in-place.
-                        obj[index] = (JsonNode)key.Description!;
+                        obj[index] = (JsonNode)description!;
                     }
                 }
 
-                if (key.HasDefaultValue)
+                if (hasDefaultValue)
                 {
-                    JsonNode? defaultValue = JsonSerializer.Serialize(key.DefaultValue, options.GetTypeInfo(typeof(object)));
-                    ConvertSchemaToObject(ref schema)[DefaultPropertyName] = defaultValue;
+                    JsonNode? defaultValueNode = JsonSerializer.Serialize(defaultValue, serializerOptions.GetTypeInfo(typeof(object)));
+                    ConvertSchemaToObject(ref schema)[DefaultPropertyName] = defaultValueNode;
                 }
 
-                if (key.IncludeSchemaKeyword)
+                if (inferenceOptions.IncludeSchemaKeyword)
                 {
                     // The $schema property must be the first keyword in the object
                     ConvertSchemaToObject(ref schema).InsertAtStart(SchemaPropertyName, (JsonNode)SchemaKeywordUri);
@@ -349,7 +309,7 @@ JsonNode TransformSchemaNode(JsonSchemaExporterContext schemaExporterContext, Js
             }
 
             // Finally, apply any user-defined transformations if specified.
-            if (key.TransformSchemaNode is { } transformer)
+            if (inferenceOptions.TransformSchemaNode is { } transformer)
             {
                 schema = transformer(ctx, schema);
             }
@@ -443,45 +403,9 @@ private static int IndexOf(this JsonObject jsonObject, string key)
         return -1;
     }
 #endif
-
     private static JsonElement ParseJsonElement(ReadOnlySpan utf8Json)
     {
         Utf8JsonReader reader = new(utf8Json);
         return JsonElement.ParseValue(ref reader);
     }
-
-    /// The equatable key used to look up cached schemas.
-    private readonly record struct SchemaGenerationKey
-    {
-        public SchemaGenerationKey(
-            Type? type,
-            string? parameterName,
-            string? description,
-            bool hasDefaultValue,
-            object? defaultValue,
-            AIJsonSchemaCreateOptions options)
-        {
-            Type = type;
-            ParameterName = parameterName;
-            Description = description;
-            HasDefaultValue = hasDefaultValue;
-            DefaultValue = defaultValue;
-            IncludeSchemaKeyword = options.IncludeSchemaKeyword;
-            DisallowAdditionalProperties = options.DisallowAdditionalProperties;
-            IncludeTypeInEnumSchemas = options.IncludeTypeInEnumSchemas;
-            RequireAllProperties = options.RequireAllProperties;
-            TransformSchemaNode = options.TransformSchemaNode;
-        }
-
-        public Type? Type { get; }
-        public string? ParameterName { get; }
-        public string? Description { get; }
-        public bool HasDefaultValue { get; }
-        public object? DefaultValue { get; }
-        public bool IncludeSchemaKeyword { get; }
-        public bool DisallowAdditionalProperties { get; }
-        public bool IncludeTypeInEnumSchemas { get; }
-        public bool RequireAllProperties { get; }
-        public Func? TransformSchemaNode { get; }
-    }
 }
diff --git a/src/Libraries/Microsoft.Extensions.AI.AzureAIInference/AzureAIChatToolJson.cs b/src/Libraries/Microsoft.Extensions.AI.AzureAIInference/AzureAIChatToolJson.cs
index 77e675c0830..7be8fee287a 100644
--- a/src/Libraries/Microsoft.Extensions.AI.AzureAIInference/AzureAIChatToolJson.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.AzureAIInference/AzureAIChatToolJson.cs
@@ -1,7 +1,6 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-using System;
 using System.Collections.Generic;
 using System.Text.Json;
 using System.Text.Json.Serialization;
@@ -11,9 +10,6 @@ namespace Microsoft.Extensions.AI;
 /// Used to create the JSON payload for an AzureAI chat tool description.
 internal sealed class AzureAIChatToolJson
 {
-    /// Gets a singleton JSON data for empty parameters. Optimization for the reasonably common case of a parameterless function.
-    public static BinaryData ZeroFunctionParametersSchema { get; } = new("""{"type":"object","required":[],"properties":{}}"""u8.ToArray());
-
     [JsonPropertyName("type")]
     public string Type { get; set; } = "object";
 
diff --git a/src/Libraries/Microsoft.Extensions.AI.AzureAIInference/AzureAIInferenceChatClient.cs b/src/Libraries/Microsoft.Extensions.AI.AzureAIInference/AzureAIInferenceChatClient.cs
index 85903beb701..511d9426c39 100644
--- a/src/Libraries/Microsoft.Extensions.AI.AzureAIInference/AzureAIInferenceChatClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.AzureAIInference/AzureAIInferenceChatClient.cs
@@ -24,9 +24,6 @@ namespace Microsoft.Extensions.AI;
 /// Represents an  for an Azure AI Inference .
 public sealed class AzureAIInferenceChatClient : IChatClient
 {
-    /// A default schema to use when a parameter lacks a pre-defined schema.
-    private static readonly JsonElement _defaultParameterSchema = JsonDocument.Parse("{}").RootElement;
-
     /// The underlying .
     private readonly ChatCompletionsClient _chatCompletionsClient;
 
@@ -376,33 +373,13 @@ private ChatCompletionsOptions ToAzureAIOptions(IList chatContents,
     /// Converts an Extensions function to an AzureAI chat tool.
     private static ChatCompletionsToolDefinition ToAzureAIChatTool(AIFunction aiFunction)
     {
-        BinaryData resultParameters = AzureAIChatToolJson.ZeroFunctionParametersSchema;
-
-        var parameters = aiFunction.Metadata.Parameters;
-        if (parameters is { Count: > 0 })
-        {
-            AzureAIChatToolJson tool = new();
-
-            foreach (AIFunctionParameterMetadata parameter in parameters)
-            {
-                tool.Properties.Add(
-                    parameter.Name,
-                    parameter.Schema is JsonElement schema ? schema : _defaultParameterSchema);
-
-                if (parameter.IsRequired)
-                {
-                    tool.Required.Add(parameter.Name);
-                }
-            }
-
-            resultParameters = BinaryData.FromBytes(
-                JsonSerializer.SerializeToUtf8Bytes(tool, JsonContext.Default.AzureAIChatToolJson));
-        }
-
+        // Map to an intermediate model so that redundant properties are skipped.
+        AzureAIChatToolJson tool = JsonSerializer.Deserialize(aiFunction.Metadata.Schema, JsonContext.Default.AzureAIChatToolJson)!;
+        BinaryData functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(tool, JsonContext.Default.AzureAIChatToolJson));
         return new(new FunctionDefinition(aiFunction.Metadata.Name)
         {
             Description = aiFunction.Metadata.Description,
-            Parameters = resultParameters,
+            Parameters = functionParameters,
         });
     }
 
diff --git a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs
index 46cbb7d7d11..4038ac8e761 100644
--- a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs
@@ -22,7 +22,6 @@ namespace Microsoft.Extensions.AI;
 /// Represents an  for Ollama.
 public sealed class OllamaChatClient : IChatClient
 {
-    private static readonly JsonElement _defaultParameterSchema = JsonDocument.Parse("{}").RootElement;
     private static readonly JsonElement _schemalessJsonResponseFormatValue = JsonDocument.Parse("\"json\"").RootElement;
 
     /// The api/chat endpoint URI.
@@ -466,20 +465,17 @@ private IEnumerable ToOllamaChatRequestMessages(ChatMe
         }
     }
 
-    private static OllamaTool ToOllamaTool(AIFunction function) => new()
+    private static OllamaTool ToOllamaTool(AIFunction function)
     {
-        Type = "function",
-        Function = new OllamaFunctionTool
+        return new()
         {
-            Name = function.Metadata.Name,
-            Description = function.Metadata.Description,
-            Parameters = new OllamaFunctionToolParameters
+            Type = "function",
+            Function = new OllamaFunctionTool
             {
-                Properties = function.Metadata.Parameters.ToDictionary(
-                    p => p.Name,
-                    p => p.Schema is JsonElement e ? e : _defaultParameterSchema),
-                Required = function.Metadata.Parameters.Where(p => p.IsRequired).Select(p => p.Name).ToList(),
-            },
-        }
-    };
+                Name = function.Metadata.Name,
+                Description = function.Metadata.Description,
+                Parameters = JsonSerializer.Deserialize(function.Metadata.Schema, JsonContext.Default.OllamaFunctionToolParameters)!,
+            }
+        };
+    }
 }
diff --git a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaFunctionToolParameters.cs b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaFunctionToolParameters.cs
index 1e01d4d5d62..9fa7d0d2adc 100644
--- a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaFunctionToolParameters.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaFunctionToolParameters.cs
@@ -10,5 +10,5 @@ internal sealed class OllamaFunctionToolParameters
 {
     public string Type { get; set; } = "object";
     public required IDictionary Properties { get; set; }
-    public required IList Required { get; set; }
+    public IList? Required { get; set; }
 }
diff --git a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMapper.ChatCompletion.cs b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMapper.ChatCompletion.cs
index 0cc75c5f34f..92e956da9e6 100644
--- a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMapper.ChatCompletion.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMapper.ChatCompletion.cs
@@ -363,7 +363,6 @@ private static AITool FromOpenAIChatTool(ChatTool chatTool)
         {
             parameters.Add(new(property.Key)
             {
-                Schema = property.Value,
                 IsRequired = openAiChatTool.Required.Contains(property.Key),
             });
         }
@@ -373,6 +372,7 @@ private static AITool FromOpenAIChatTool(ChatTool chatTool)
             Description = chatTool.FunctionDescription,
             AdditionalProperties = additionalProperties,
             Parameters = parameters,
+            Schema = JsonSerializer.SerializeToElement(openAiChatTool, OpenAIJsonContext.Default.OpenAIChatToolJson),
             ReturnParameter = new()
             {
                 Description = "Return parameter",
@@ -398,28 +398,10 @@ private static ChatTool ToOpenAIChatTool(AIFunction aiFunction)
             strictObj is bool strictValue ?
             strictValue : null;
 
-        BinaryData resultParameters = OpenAIChatToolJson.ZeroFunctionParametersSchema;
-
-        var parameters = aiFunction.Metadata.Parameters;
-        if (parameters is { Count: > 0 })
-        {
-            OpenAIChatToolJson tool = new();
-
-            foreach (AIFunctionParameterMetadata parameter in parameters)
-            {
-                tool.Properties.Add(parameter.Name, parameter.Schema is JsonElement e ? e : _defaultParameterSchema);
-
-                if (parameter.IsRequired)
-                {
-                    _ = tool.Required.Add(parameter.Name);
-                }
-            }
-
-            resultParameters = BinaryData.FromBytes(
-                JsonSerializer.SerializeToUtf8Bytes(tool, OpenAIJsonContext.Default.OpenAIChatToolJson));
-        }
-
-        return ChatTool.CreateFunctionTool(aiFunction.Metadata.Name, aiFunction.Metadata.Description, resultParameters, strict);
+        // Map to an intermediate model so that redundant properties are skipped.
+        OpenAIChatToolJson tool = JsonSerializer.Deserialize(aiFunction.Metadata.Schema, OpenAIJsonContext.Default.OpenAIChatToolJson)!;
+        BinaryData functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(tool, OpenAIJsonContext.Default.OpenAIChatToolJson));
+        return ChatTool.CreateFunctionTool(aiFunction.Metadata.Name, aiFunction.Metadata.Description, functionParameters, strict);
     }
 
     private static UsageDetails FromOpenAIUsage(ChatTokenUsage tokenUsage)
@@ -596,9 +578,6 @@ private static FunctionCallContent ParseCallContentFromBinaryData(BinaryData ut8
     /// Used to create the JSON payload for an OpenAI chat tool description.
     public sealed class OpenAIChatToolJson
     {
-        /// Gets a singleton JSON data for empty parameters. Optimization for the reasonably common case of a parameterless function.
-        public static BinaryData ZeroFunctionParametersSchema { get; } = new("""{"type":"object","required":[],"properties":{}}"""u8.ToArray());
-
         [JsonPropertyName("type")]
         public string Type { get; set; } = "object";
 
diff --git a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIRealtimeExtensions.cs b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIRealtimeExtensions.cs
index c47cfc52c21..db12baf962d 100644
--- a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIRealtimeExtensions.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIRealtimeExtensions.cs
@@ -18,8 +18,6 @@ namespace Microsoft.Extensions.AI;
 /// 
 public static class OpenAIRealtimeExtensions
 {
-    private static readonly JsonElement _defaultParameterSchema = JsonDocument.Parse("{}").RootElement;
-
     /// 
     /// Converts a  into a  so that
     /// it can be used with .
@@ -29,22 +27,13 @@ public static ConversationFunctionTool ToConversationFunctionTool(this AIFunctio
     {
         _ = Throw.IfNull(aiFunction);
 
-        var parametersSchema = new ConversationFunctionToolParametersSchema
-        {
-            Type = "object",
-            Properties = aiFunction.Metadata.Parameters
-                .ToDictionary(p => p.Name, GetParameterSchema),
-            Required = aiFunction.Metadata.Parameters
-                .Where(p => p.IsRequired)
-                .Select(p => p.Name),
-        };
-
+        ConversationFunctionToolParametersSchema functionToolSchema = JsonSerializer.Deserialize(aiFunction.Metadata.Schema, OpenAIJsonContext.Default.ConversationFunctionToolParametersSchema)!;
+        BinaryData functionParameters = new(JsonSerializer.SerializeToUtf8Bytes(functionToolSchema, OpenAIJsonContext.Default.ConversationFunctionToolParametersSchema));
         return new ConversationFunctionTool
         {
             Name = aiFunction.Metadata.Name,
             Description = aiFunction.Metadata.Description,
-            Parameters = new BinaryData(JsonSerializer.SerializeToUtf8Bytes(
-                parametersSchema, OpenAIJsonContext.Default.ConversationFunctionToolParametersSchema))
+            Parameters = functionParameters
         };
     }
 
@@ -95,15 +84,6 @@ public static async Task HandleToolCallsAsync(
         }
     }
 
-    private static JsonElement GetParameterSchema(AIFunctionParameterMetadata parameterMetadata)
-    {
-        return parameterMetadata switch
-        {
-            { Schema: JsonElement jsonElement } => jsonElement,
-            _ => _defaultParameterSchema,
-        };
-    }
-
     private static async Task GetFunctionCallOutputAsync(
         this ConversationItemStreamingFinishedUpdate update,
         IReadOnlyList tools,
diff --git a/src/Libraries/Microsoft.Extensions.AI/Functions/AIFunctionFactory.cs b/src/Libraries/Microsoft.Extensions.AI/Functions/AIFunctionFactory.cs
index 854ec2f162a..a3084882d75 100644
--- a/src/Libraries/Microsoft.Extensions.AI/Functions/AIFunctionFactory.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/Functions/AIFunctionFactory.cs
@@ -265,9 +265,10 @@ static bool IsAsyncMethod(MethodInfo method)
             Type returnType = GetReturnMarshaller(method, out _returnMarshaller);
             _returnTypeInfo = returnType != typeof(void) ? options.SerializerOptions.GetTypeInfo(returnType) : null;
 
+            string? description = options.Description ?? method.GetCustomAttribute(inherit: true)?.Description;
             Metadata = new AIFunctionMetadata(functionName)
             {
-                Description = options.Description ?? method.GetCustomAttribute(inherit: true)?.Description ?? string.Empty,
+                Description = description,
                 Parameters = options.Parameters ?? parameterMetadata!,
                 ReturnParameter = options.ReturnParameter ?? new()
                 {
@@ -277,6 +278,12 @@ static bool IsAsyncMethod(MethodInfo method)
                 },
                 AdditionalProperties = options.AdditionalProperties ?? EmptyReadOnlyDictionary.Instance,
                 JsonSerializerOptions = options.SerializerOptions,
+                Schema = AIJsonUtilities.CreateFunctionJsonSchema(
+                    title: functionName,
+                    description: description,
+                    parameters: options.Parameters ?? parameterMetadata,
+                    options.SerializerOptions,
+                    options.SchemaCreateOptions)
             };
         }
 
@@ -419,14 +426,6 @@ static bool IsAsyncMethod(MethodInfo method)
                 DefaultValue = parameter.HasDefaultValue ? parameter.DefaultValue : null,
                 IsRequired = !parameter.IsOptional,
                 ParameterType = parameter.ParameterType,
-                Schema = AIJsonUtilities.CreateParameterJsonSchema(
-                    parameter.ParameterType,
-                    parameter.Name,
-                    description,
-                    parameter.HasDefaultValue,
-                    parameter.DefaultValue,
-                    options.SerializerOptions,
-                    options.SchemaCreateOptions)
             };
         }
 
diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionMetadataTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionMetadataTests.cs
index a1aa48bd115..08397144632 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionMetadataTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionMetadataTests.cs
@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Text.Json;
 using Xunit;
 
 namespace Microsoft.Extensions.AI;
@@ -26,7 +27,7 @@ public void Constructor_String_PropsDefaulted()
         Assert.Empty(f.Parameters);
 
         Assert.NotNull(f.ReturnParameter);
-        Assert.Null(f.ReturnParameter.Schema);
+        Assert.True(JsonElement.DeepEquals(f.ReturnParameter.Schema, JsonDocument.Parse("{}").RootElement));
         Assert.Null(f.ReturnParameter.ParameterType);
         Assert.Null(f.ReturnParameter.Description);
 
diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionParameterMetadataTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionParameterMetadataTests.cs
index 23c33ecf07a..3eef60269a8 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionParameterMetadataTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionParameterMetadataTests.cs
@@ -2,7 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
-using System.Text.Json;
 using Xunit;
 
 namespace Microsoft.Extensions.AI;
@@ -26,7 +25,6 @@ public void Constructor_String_PropsDefaulted()
         Assert.Null(p.DefaultValue);
         Assert.False(p.IsRequired);
         Assert.Null(p.ParameterType);
-        Assert.Null(p.Schema);
     }
 
     [Fact]
@@ -39,7 +37,6 @@ public void Constructor_Copy_PropsPropagated()
             DefaultValue = 42,
             IsRequired = true,
             ParameterType = typeof(int),
-            Schema = JsonDocument.Parse("""{"type":"integer"}"""),
         };
 
         AIFunctionParameterMetadata p2 = new(p1);
@@ -49,7 +46,6 @@ public void Constructor_Copy_PropsPropagated()
         Assert.Equal(p1.DefaultValue, p2.DefaultValue);
         Assert.Equal(p1.IsRequired, p2.IsRequired);
         Assert.Equal(p1.ParameterType, p2.ParameterType);
-        Assert.Equal(p1.Schema, p2.Schema);
     }
 
     [Fact]
@@ -62,7 +58,6 @@ public void Constructor_Copy_PropsPropagatedAndOverwritten()
             DefaultValue = 42,
             IsRequired = true,
             ParameterType = typeof(int),
-            Schema = JsonDocument.Parse("""{"type":"integer"}"""),
         };
 
         AIFunctionParameterMetadata p2 = new(p1)
@@ -72,7 +67,6 @@ public void Constructor_Copy_PropsPropagatedAndOverwritten()
             DefaultValue = 43,
             IsRequired = false,
             ParameterType = typeof(long),
-            Schema = JsonDocument.Parse("""{"type":"number"}"""),
         };
 
         Assert.Equal("description2", p2.Description);
diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionReturnParameterMetadataTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionReturnParameterMetadataTests.cs
index bb5bbeec03a..d4721501093 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionReturnParameterMetadataTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Functions/AIFunctionReturnParameterMetadataTests.cs
@@ -14,7 +14,7 @@ public void Constructor_PropsDefaulted()
         AIFunctionReturnParameterMetadata p = new();
         Assert.Null(p.Description);
         Assert.Null(p.ParameterType);
-        Assert.Null(p.Schema);
+        Assert.True(JsonElement.DeepEquals(p.Schema, JsonDocument.Parse("{}").RootElement));
     }
 
     [Fact]
@@ -24,7 +24,7 @@ public void Constructor_Copy_PropsPropagated()
         {
             Description = "description",
             ParameterType = typeof(int),
-            Schema = JsonDocument.Parse("""{"type":"integer"}"""),
+            Schema = JsonDocument.Parse("""{"type":"integer"}""").RootElement,
         };
 
         AIFunctionReturnParameterMetadata p2 = new(p1);
diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Utilities/AIJsonUtilitiesTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Utilities/AIJsonUtilitiesTests.cs
index 6e782273f8b..4d654e042ff 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Utilities/AIJsonUtilitiesTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Utilities/AIJsonUtilitiesTests.cs
@@ -225,30 +225,32 @@ public class PocoWithTypesWithOpenAIUnsupportedKeywords
     }
 
     [Fact]
-    public static void ResolveParameterJsonSchema_ReturnsExpectedValue()
+    public static void CreateFunctionJsonSchema_ReturnsExpectedValue()
     {
         JsonSerializerOptions options = new(JsonSerializerOptions.Default);
         AIFunction func = AIFunctionFactory.Create((int x, int y) => x + y, serializerOptions: options);
 
         AIFunctionMetadata metadata = func.Metadata;
         AIFunctionParameterMetadata param = metadata.Parameters[0];
-        JsonElement generatedSchema = Assert.IsType(param.Schema);
 
-        JsonElement resolvedSchema;
-        resolvedSchema = AIJsonUtilities.ResolveParameterJsonSchema(param, metadata, options);
-        Assert.True(JsonElement.DeepEquals(generatedSchema, resolvedSchema));
+        JsonElement resolvedSchema = AIJsonUtilities.CreateFunctionJsonSchema(title: func.Metadata.Name, description: func.Metadata.Description, parameters: func.Metadata.Parameters);
+        Assert.True(JsonElement.DeepEquals(resolvedSchema, func.Metadata.Schema));
     }
 
     [Fact]
-    public static void CreateParameterJsonSchema_TreatsIntegralTypesAsInteger_EvenWithAllowReadingFromString()
+    public static void CreateFunctionJsonSchema_TreatsIntegralTypesAsInteger_EvenWithAllowReadingFromString()
     {
         JsonSerializerOptions options = new(JsonSerializerOptions.Default) { NumberHandling = JsonNumberHandling.AllowReadingFromString };
         AIFunction func = AIFunctionFactory.Create((int a, int? b, long c, short d, float e, double f, decimal g) => { }, serializerOptions: options);
 
         AIFunctionMetadata metadata = func.Metadata;
-        foreach (var param in metadata.Parameters)
+        JsonElement schemaParameters = func.Metadata.Schema.GetProperty("properties");
+        Assert.Equal(metadata.Parameters.Count, schemaParameters.GetPropertyCount());
+
+        int i = 0;
+        foreach (JsonProperty property in schemaParameters.EnumerateObject())
         {
-            string numericType = Type.GetTypeCode(param.ParameterType) is TypeCode.Double or TypeCode.Single or TypeCode.Decimal
+            string numericType = Type.GetTypeCode(metadata.Parameters[i].ParameterType) is TypeCode.Double or TypeCode.Single or TypeCode.Decimal
                 ? "number"
                 : "integer";
 
@@ -258,8 +260,9 @@ public static void CreateParameterJsonSchema_TreatsIntegralTypesAsInteger_EvenWi
                 }
                 """).RootElement;
 
-            JsonElement actualSchema = Assert.IsType(param.Schema);
+            JsonElement actualSchema = property.Value;
             Assert.True(JsonElement.DeepEquals(expected, actualSchema));
+            i++;
         }
     }
 
diff --git a/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAISerializationTests.cs b/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAISerializationTests.cs
index f636063804e..72461ce2a48 100644
--- a/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAISerializationTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAISerializationTests.cs
@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Linq;
 using System.Text;
 using System.Text.Json;
 using System.Text.Json.Nodes;
@@ -379,7 +380,8 @@ public static async Task RequestDeserialization_ToolCall()
         Assert.Equal("personName", parameter.Name);
         Assert.True(parameter.IsRequired);
 
-        JsonObject parameterSchema = Assert.IsType(JsonNode.Parse(Assert.IsType(parameter.Schema).GetRawText()));
+        JsonObject parametersSchema = Assert.IsType(JsonNode.Parse(function.Metadata.Schema.GetProperty("properties").GetRawText()));
+        var parameterSchema = Assert.IsType(Assert.Single(parametersSchema.Select(kvp => kvp.Value)));
         Assert.Equal(2, parameterSchema.Count);
         Assert.Equal("The person whose age is being requested", (string)parameterSchema["description"]!);
         Assert.Equal("string", (string)parameterSchema["type"]!);