Normalización de Esquemas
ADK-Rust normaliza automáticamente los esquemas de herramientas MCP para cada proveedor LLM en el momento de la solicitud. Esto significa que las herramientas MCP funcionan sin problemas en Gemini, OpenAI, Anthropic y otros proveedores sin ajustes manuales del esquema.
Cómo Funciona
MCP Server → raw JSON Schema → McpToolset (stores verbatim)
↓
Model.generate_content()
↓
SchemaAdapter.normalize_schema()
↓
Provider API (Gemini/OpenAI/Anthropic)
- McpToolset descubre herramientas y almacena sus
inputSchemasin modificaciones - Cuando el modelo construye una solicitud, llama a
schema_adapter().normalize_schema()en el esquema de cada herramienta - Cada proveedor tiene su propio adaptador que aplica solo las transformaciones que su API requiere
- Los resultados se almacenan en caché por hash de contenido para evitar la normalización redundante
Comportamiento del Proveedor
| Característica | Gemini | OpenAI Estricto | OpenAI | Anthropic | Generic |
|---|---|---|---|---|---|
$ref resolución | ✅ Inlines | ❌ Preserves | ❌ Preserves | ❌ Preserves | ❌ Preserves |
anyOf/oneOf | Colapsa | Preserva | Preserva | Preserva | Preserva |
allOf | Fusiona | Conserva | Conserva | Conserva | Conserva |
additionalProperties | Elimina | Establece false | Conserva | Conserva | Conserva |
| Arreglos de tipos | Colapsa | Preserva | Preserva | Preserva | Preserva |
$schema | Tiras | Tiras | Tiras | Tiras | Tiras |
if/then/else | Tiras | Tiras | Tiras | Tiras | Tiras |
const | → enum | → enum | → enum | Preserva | → enum |
No compatible format | 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 SchemaAdapter Trait
Todos los adaptadores implementan este trait 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 Llm implementación devuelve su adaptador a través de schema_adapter():
use adk_core::Llm;
let model = GeminiModel::new(&api_key, "gemini-2.5-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 a función 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();
Pipeline de transformación:
- Resuelve
$ref(en línea desde definiciones, rompe ciclos a profundidad 10) - Elimina
$schema - Colapsa
anyOf/oneOf→ primer sub-esquema no nulo - Fusiona
allOfsub-esquemas - Colapsa arreglos de tipos (
["string", "null"]→"string") - Elimina
if/then/else - Convierte
const→enumde un solo elemento - Elimina nulo de los arreglos
enum - Añade
type: "object"implícito - Elimina palabras clave no soportadas
- Elimina valores
formatno soportados - Aplica profundidad de anidamiento (5 niveles)
- Elimina
definitions/$defs
OpenAiStrictSchemaAdapter
Preserva la estructura del esquema mientras añade additionalProperties: false para 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 paso directo — Anthropic soporta la mayoría de las características del esquema JSON:
use adk_model::anthropic::AnthropicSchemaAdapter;
let adapter = AnthropicSchemaAdapter;
GenericSchemaAdapter
Default for unknown providers (Ollama, DeepSeek, etc.):
use adk_core::GenericSchemaAdapter;
let adapter = GenericSchemaAdapter;
Caché de Esquemas
Los esquemas normalizados se almacenan en caché por hash de contenido para evitar cálculos redundantes:
use adk_core::{SchemaCache, GenericSchemaAdapter, SchemaAdapter};
use serde_json::json;
let cache = SchemaCache::new();
let adapter = GenericSchemaAdapter;
let schema = json!({"type": "object", "properties": {"name": {"type": "string"}}});
// First call normalizes and caches
let result = cache.get_or_normalize(&schema, &adapter);
// Subsequent calls return cached result (no re-normalization)
let cached = cache.get_or_normalize(&schema, &adapter);
// Invalidate when tools change
cache.clear();
La caché reside en las instancias del modelo y se utiliza automáticamente durante generate_content().
Truncamiento de Nombres de Herramientas
Todos los adaptadores truncan los nombres de herramientas que exceden los 64 bytes en los límites de caracteres válidos 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"cuandopropertiesexisteconvert_const_to_enum— convierteconstaenumde un solo elementostrip_unsupported_formats— elimina valores de formato que no están en la lista de permitidosstrip_null_from_enum— elimina nulos de los arrays de enumeracióntruncate_tool_name— trunca en el límite UTF-8resolve_refs— inserta en línea las referencias$refde las definicionescollapse_combiners— colapsaanyOf/oneOfal primer no nulomerge_all_of— fusiona los subesquemasallOfcollapse_type_arrays— colapsa["string", "null"]a"string"enforce_nesting_depth— reemplaza esquemas profundos con{"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 — demuestra la lógica de normalización localmente.
Migración desde sanitize_schema
Si anteriormente dependía de McpToolset que devolvía esquemas pre-sanitizados:
- No se necesitan cambios en el código para uso estándar. El trait
ToolsetAPI no ha cambiado. - Los esquemas ahora se normalizan en el momento de la solicitud por el adaptador del modelo, no en el registro de la herramienta.
- Si estaba llamando a
parameters_schema()y esperaba una salida con formato Gemini, ahora se devuelve el esquema sin procesar en su lugar. Utilice elSchemaAdapterapropiado para normalizarlo usted mismo si es necesario.
Anterior: ← MCP Herramientas | Siguiente: Sesiones →