Normalización de esquemas
ADK-Rust normaliza automáticamente los esquemas de herramientas de MCP para cada proveedor de LLM en el momento de la solicitud. Esto significa que las herramientas de MCP funcionan sin problemas con Gemini, OpenAI, Anthropic y otros proveedores sin necesidad de ajustar manualmente los esquemas.
Cómo funciona
MCP Server → raw JSON Schema → McpToolset (stores verbatim)
↓
Model.generate_content()
↓
SchemaAdapter.normalize_schema()
↓
Provider API (Gemini/OpenAI/Anthropic)
- McpToolset descubre las herramientas y almacena sus elementos
inputSchemasin modificaciones - Cuando el modelo crea una solicitud, llama a
schema_adapter().normalize_schema()en el esquema de cada herramienta - Cada proveedor tiene su propio adaptador que aplica únicamente las transformaciones que requiere su API
- Los resultados se almacenan en caché mediante un hash de contenido para evitar normalizaciones redundantes
Comportamiento según el proveedor
| Característica | Gemini | OpenAI Estricto | OpenAI | Anthropic | Genérico |
|---|---|---|---|---|---|
Resolución de $ref | ✅ Integra | ❌ Conserva | ❌ Conserva | ❌ Conserva | ❌ Conserva |
anyOf/oneOf | Combina | Conserva | Conserva | Conserva | Conserva |
allOf | Fusiona | Conserva | Conserva | Conserva | Conserva |
additionalProperties | Elimina | Establece false | Conserva | Conserva | Conserva |
| Matrices de tipos | Colapsa | Conserva | Conserva | Conserva | Conserva |
$schema | Elimina | Elimina | Elimina | Elimina | Elimina |
if/then/else | Elimina | Elimina | Elimina | Elimina | Elimina |
const | → enum | → enum | → enum | Conserva | → enum |
format no compatible | Elimina | Elimina | Elimina | Conserva | Elimina |
| Límite de profundidad de anidamiento | 5 niveles | Ninguno | Ninguno | Ninguno | Ninguno |
exclusiveMin/Max | Elimina | Conserva | Conserva | Conserva | Conserva |
El rasgo SchemaAdapter
Todos los adaptadores implementan este rasgo de adk-core:
use serde_json::Value;
use std::borrow::Cow;
pub trait SchemaAdapter: Send + Sync + std::fmt::Debug {
/// Normalize a raw JSON Schema for this provider.
fn normalize_schema(&self, schema: Value) -> Value;
/// Normalize a tool name (default: truncate to 64 bytes at UTF-8 boundary).
fn normalize_tool_name<'a>(&self, name: &'a str) -> Cow<'a, str>;
/// Fallback schema when no parameters_schema is provided.
fn empty_schema(&self) -> Value;
}
Cada implementación de Llm devuelve su adaptador mediante schema_adapter():
use adk_core::Llm;
let model = GeminiModel::new(&api_key, "gemini-3.7-flash")?;
let adapter = model.schema_adapter(); // Returns &GeminiSchemaAdapter
Adaptadores disponibles
GeminiSchemaAdapter
El adaptador más agresivo. Aplica todas las transformaciones destructivas requeridas por la llamada de funciones de Gemini API:
use adk_gemini::schema_adapter::GeminiSchemaAdapter;
use adk_core::SchemaAdapter;
// Standard Gemini API
let adapter = GeminiSchemaAdapter::new();
// Vertex AI (sets additionalProperties: false instead of removing)
let adapter = GeminiSchemaAdapter::vertex_ai();
Flujo de transformación:
- Resolver
$ref(insertar definiciones en línea y romper ciclos en la profundidad 10) - Eliminar
$schema - Contraer
anyOf/oneOf→ primer subesquema no nulo - Combinar los subesquemas de
allOf - Contraer matrices de tipos (
["string", "null"]→"string") - Eliminar
if/then/else - Convertir
const→enumde un solo elemento - Eliminar los valores nulos de las matrices
enum - Añadir
type: "object"implícito - Eliminar palabras clave no compatibles
- Eliminar los valores
formatno compatibles - Aplicar un límite a la profundidad de anidamiento (5 niveles)
- Eliminar
definitions/$defs
OpenAiStrictSchemaAdapter
Conserva la estructura del esquema y añade additionalProperties: false para las salidas estructuradas:
use adk_model::openai::OpenAiStrictSchemaAdapter;
use adk_core::SchemaAdapter;
let adapter = OpenAiStrictSchemaAdapter;
OpenAiSchemaAdapter
Correcciones seguras mínimas para el modo no estricto:
use adk_model::openai::OpenAiSchemaAdapter;
let adapter = OpenAiSchemaAdapter;
AnthropicSchemaAdapter
Casi de paso directo: Anthropic admite la mayoría de las funciones del esquema JSON:
use adk_model::anthropic::AnthropicSchemaAdapter;
let adapter = AnthropicSchemaAdapter;
GenericSchemaAdapter
Predeterminado para proveedores desconocidos (Ollama, DeepSeek, etc.):
use adk_core::GenericSchemaAdapter;
let adapter = GenericSchemaAdapter;
Almacenamiento en caché de esquemas
Los esquemas normalizados se almacenan en caché mediante un hash de contenido para evitar cálculos redundantes:
use adk_core::{GenericSchemaAdapter, SchemaCache};
use serde_json::json;
use std::sync::Arc;
let cache = SchemaCache::for_adapter(Arc::new(GenericSchemaAdapter));
let schema = json!({"type": "object", "properties": {"name": {"type": "string"}}});
// First call normalizes and caches
let result = cache.normalize(&schema);
// Subsequent calls return cached result (no re-normalization)
let cached = cache.normalize(&schema);
// Invalidate when tools change
cache.clear();
Cada caché posee una instancia de adaptador, por lo que las entradas normalizadas para distintos proveedores o configuraciones de adaptador no pueden colisionar. Los clientes de proveedores utilizan estas cachés automáticamente durante generate_content().
Truncamiento de nombres de herramientas
Todos los adaptadores truncan los nombres de herramientas que superan los 64 bytes en límites válidos de caracteres de UTF-8:
use adk_core::SchemaAdapter;
use adk_gemini::schema_adapter::GeminiSchemaAdapter;
let adapter = GeminiSchemaAdapter::new();
// Short names pass through unchanged
let name = adapter.normalize_tool_name("get_weather");
assert_eq!(name, "get_weather");
// Long names are truncated to 64 bytes
let long = "mcp_server_github_com_organization_repository_pull_request_review_comments";
let truncated = adapter.normalize_tool_name(long);
assert!(truncated.len() <= 64);
// Multi-byte characters are never split
let emoji_name = "🔧_tool_名前が長い";
let result = adapter.normalize_tool_name(emoji_name);
assert!(std::str::from_utf8(result.as_bytes()).is_ok());
Utilidades compartidas
El módulo adk_core::schema_utils proporciona funciones de transformación componibles que los adaptadores utilizan internamente:
use adk_core::schema_utils;
use serde_json::json;
let mut schema = json!({
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"status": { "const": "active" },
"email": { "type": "string", "format": "hostname" }
},
"if": { "properties": { "x": { "type": "number" } } },
"then": { "required": ["x"] }
});
// Apply individual transforms
schema_utils::strip_schema_keyword(&mut schema);
schema_utils::strip_conditional_keywords(&mut schema);
schema_utils::convert_const_to_enum(&mut schema);
schema_utils::strip_unsupported_formats(&mut schema, &["date-time", "email", "uri"]);
Utilidades disponibles:
strip_schema_keyword— elimina$schemastrip_conditional_keywords— eliminaif/then/elseadd_implicit_object_type— añadetype: "object"cuando existepropertiesconvert_const_to_enum— convierteconstenenumde un solo elementostrip_unsupported_formats— elimina los valores de formato que no están en la lista de permitidosstrip_null_from_enum— elimina null de las matrices de enumeracióntruncate_tool_name— trunca en el límite UTF-8resolve_refs— inserta en línea las referencias$refde las definicionescollapse_combiners— reduceanyOf/oneOfal primer valor que no sea nullmerge_all_of— combina los subesquemasallOfcollapse_type_arrays— reduce["string", "null"]a"string"enforce_nesting_depth— reemplaza los esquemas profundos por{"type": "object"}
Adaptadores personalizados
Implementa SchemaAdapter para proveedores personalizados:
use adk_core::{SchemaAdapter, schema_utils};
use serde_json::Value;
use std::borrow::Cow;
#[derive(Debug)]
struct MyProviderAdapter;
impl SchemaAdapter for MyProviderAdapter {
fn normalize_schema(&self, mut schema: Value) -> Value {
// Apply only the transforms your provider needs
schema_utils::strip_schema_keyword(&mut schema);
schema_utils::strip_conditional_keywords(&mut schema);
schema_utils::add_implicit_object_type(&mut schema);
// Keep everything else as-is
schema
}
}
Ejemplo
Ejecuta la demostración de normalización de esquemas para ver todos los adaptadores en acción:
cd examples/schema_normalization
cargo run
No se necesitan claves API; esto demuestra la lógica de normalización localmente.
Migración desde sanitize_schema
Si anteriormente dependías de que McpToolset devolviera esquemas previamente saneados:
- No se necesitan cambios de código para el uso estándar. El trait
ToolsetAPI no ha cambiado. - Ahora los esquemas se normalizan en el momento de la solicitud mediante el adaptador del modelo, no durante el registro de la herramienta.
- Si llamabas a
parameters_schema()y esperabas una salida con formato de Gemini, ahora se devuelve el esquema sin procesar. Usa elSchemaAdapterapropiado para normalizarlo por tu cuenta si es necesario.
Anterior: ← MCP Herramientas | Siguiente: Sesiones →