diff --git a/src/services/search/Elastic.Documentation.Search/Common/ElasticsearchClientAccessor.cs b/src/services/search/Elastic.Documentation.Search/Common/ElasticsearchClientAccessor.cs
index fbb9edd72..b06d06166 100644
--- a/src/services/search/Elastic.Documentation.Search/Common/ElasticsearchClientAccessor.cs
+++ b/src/services/search/Elastic.Documentation.Search/Common/ElasticsearchClientAccessor.cs
@@ -2,7 +2,6 @@
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information
-using System.Text.Json.Serialization.Metadata;
using Elastic.Clients.Elasticsearch;
using Elastic.Clients.Elasticsearch.Serialization;
using Elastic.Documentation.Configuration;
@@ -65,18 +64,12 @@ SearchConfiguration searchConfiguration
? new BasicAuthentication(username, password)
: null!;
- // Combine the contract's source-gen context with a reflection fallback so that
- // internal package types (e.g. RuleQueryMatchCriteria from the Elasticsearch impl
- // package) are still serializable when the ES client delegates to the source serializer.
- var resolver = JsonTypeInfoResolver.Combine(
- SourceGenerationContext.Default,
- new DefaultJsonTypeInfoResolver()
- );
-
_clientSettings = new ElasticsearchClientSettings(
_nodePool,
- sourceSerializer: (_, settings) => new DefaultSourceSerializer(settings, resolver, null)
- )
+ sourceSerializer: (_, settings) => new DefaultSourceSerializer(
+ settings,
+ ElasticsearchClientJsonResolver.Default,
+ static jsonOptions => jsonOptions.Converters.Add(RuleQueryMatchCriteriaJsonConverterFactory.Instance)))
.DefaultIndex(SearchIndex)
.Authentication(auth);
diff --git a/src/services/search/Elastic.Documentation.Search/Common/ElasticsearchClientJsonResolver.cs b/src/services/search/Elastic.Documentation.Search/Common/ElasticsearchClientJsonResolver.cs
new file mode 100644
index 000000000..95fb70d1c
--- /dev/null
+++ b/src/services/search/Elastic.Documentation.Search/Common/ElasticsearchClientJsonResolver.cs
@@ -0,0 +1,27 @@
+// Licensed to Elasticsearch B.V under one or more agreements.
+// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information
+
+using System.Text.Json.Serialization.Metadata;
+using Elastic.Documentation.Serialization;
+using InternalSearch = Elastic.Internal.Search;
+
+namespace Elastic.Documentation.Search.Common;
+
+///
+/// Combined JSON type info resolver for the shared Elasticsearch client: external search contract types,
+/// docs-builder document metadata, and internal query-rule criteria from the Elasticsearch search package.
+///
+internal static class ElasticsearchClientJsonResolver
+{
+ public static IJsonTypeInfoResolver Default { get; } = Create();
+
+ private static IJsonTypeInfoResolver Create()
+ {
+ var combined = JsonTypeInfoResolver.Combine(
+ InternalSearch.SourceGenerationContext.Default,
+ SourceGenerationContext.Default);
+
+ return new RuleQueryMatchCriteriaTypeInfoResolver(combined);
+ }
+}
diff --git a/src/services/search/Elastic.Documentation.Search/Common/RuleQueryMatchCriteriaJsonConverter.cs b/src/services/search/Elastic.Documentation.Search/Common/RuleQueryMatchCriteriaJsonConverter.cs
new file mode 100644
index 000000000..eb34abae9
--- /dev/null
+++ b/src/services/search/Elastic.Documentation.Search/Common/RuleQueryMatchCriteriaJsonConverter.cs
@@ -0,0 +1,76 @@
+// Licensed to Elasticsearch B.V under one or more agreements.
+// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information
+
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Elastic.Documentation.Search.Common;
+
+///
+/// Serializes Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria, which is internal to the
+/// Elasticsearch search package and therefore not covered by public types.
+///
+internal sealed class RuleQueryMatchCriteriaJsonConverterFactory : JsonConverterFactory
+{
+ public static readonly RuleQueryMatchCriteriaJsonConverterFactory Instance = new();
+
+ [DynamicDependency(
+ DynamicallyAccessedMemberTypes.All,
+ "Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria",
+ "Elastic.Internal.Search.Elasticsearch")]
+ private static readonly Type RuleQueryMatchCriteriaType = Type.GetType(
+ "Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria, Elastic.Internal.Search.Elasticsearch",
+ throwOnError: true)!;
+
+ public override bool CanConvert(Type typeToConvert) => typeToConvert == RuleQueryMatchCriteriaType;
+
+ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) =>
+ RuleQueryMatchCriteriaJsonConverter.Instance;
+}
+
+internal sealed class RuleQueryMatchCriteriaJsonConverter : JsonConverter