Schema-Normalisierung
ADK-Rust normalisiert MCP-Tool-Schemas für jeden LLM-Provider zum Zeitpunkt der Anfrage automatisch. Das bedeutet, dass MCP-Tools nahtlos über Gemini, OpenAI, Anthropic und andere Provider hinweg funktionieren, ohne dass das Schema manuell angepasst werden muss.
Funktionsweise
MCP Server → raw JSON Schema → McpToolset (stores verbatim)
↓
Model.generate_content()
↓
SchemaAdapter.normalize_schema()
↓
Provider API (Gemini/OpenAI/Anthropic)
- McpToolset ermittelt Tools und speichert deren unverändertes rohes
inputSchema - Wenn das Modell eine Anfrage erstellt, ruft es
schema_adapter().normalize_schema()für das Schema jedes Tools auf - Jeder Provider verfügt über einen eigenen Adapter, der nur die von seinem API benötigten Transformationen anwendet
- Die Ergebnisse werden anhand eines Inhalts-Hashes zwischengespeichert, um redundante Normalisierungen zu vermeiden
Providerverhalten
| Funktion | Gemini | OpenAI Strict | OpenAI | Anthropic | Allgemein |
|---|---|---|---|---|---|
$ref Auflösung | ✅ Fügt inline ein | ❌ Behält bei | ❌ Behält bei | ❌ Behält bei | ❌ Behält bei |
anyOf/oneOf | Fasst zusammen | Behält bei | Behält bei | Behält bei | Behält bei |
allOf | Führt zusammen | Bewahrt | Bewahrt | Bewahrt | Bewahrt |
additionalProperties | Entfernt | Setzt false | Bewahrt | Bewahrt | Bewahrt |
| Typ-Arrays | Fasst zusammen | Bewahrt | Bewahrt | Bewahrt | Bewahrt |
$schema | Entfernt | Entfernt | Entfernt | Entfernt | Entfernt |
if/then/else | Entfernt | Entfernt | Entfernt | Entfernt | Entfernt |
const | → enum | → enum | → enum | Behält bei | → enum |
Nicht unterstütztes format | Entfernt | Entfernt | Entfernt | Behält bei | Entfernt |
| Verschachtelungstiefenlimit | 5 Ebenen | Keine | Keine | Keine | Keine |
exclusiveMin/Max | Entfernt | Bewahrt | Bewahrt | Bewahrt | Bewahrt |
Der SchemaAdapter-Trait
Alle Adapter implementieren diesen Trait aus 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;
}
Jede Implementierung von Llm gibt ihren Adapter über schema_adapter() zurück:
use adk_core::Llm;
let model = GeminiModel::new(&api_key, "gemini-3.7-flash")?;
let adapter = model.schema_adapter(); // Returns &GeminiSchemaAdapter
Verfügbare Adapter
GeminiSchemaAdapter
Der aggressivste Adapter. Wendet alle destruktiven Transformationen an, die für Geminis Function-Calling-API erforderlich sind:
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();
Transformationspipeline:
$refauflösen (inline aus Definitionen, Zyklen ab Tiefe 10 unterbrechen)$schemaentfernenanyOf/oneOfauf das erste Sub-Schema ungleich null reduzierenallOf-Sub-Schemas zusammenführen- Typ-Arrays reduzieren (
["string", "null"]→"string") if/then/elseentfernenconstin ein ein-elementigesenumumwandeln- Null aus
enum-Arrays entfernen - Implizites
type: "object"hinzufügen - Nicht unterstützte Schlüsselwörter entfernen
- Nicht unterstützte
format-Werte entfernen - Verschachtelungstiefe erzwingen (5 Ebenen)
definitions/$defsentfernen
OpenAiStrictSchemaAdapter
Bewahrt die Struktur des Schemas und fügt additionalProperties: false für strukturierte Ausgaben hinzu:
use adk_model::openai::OpenAiStrictSchemaAdapter;
use adk_core::SchemaAdapter;
let adapter = OpenAiStrictSchemaAdapter;
OpenAiSchemaAdapter
Minimale sichere Korrekturen für den nicht-strikten Modus:
use adk_model::openai::OpenAiSchemaAdapter;
let adapter = OpenAiSchemaAdapter;
AnthropicSchemaAdapter
Nahezu unverändert – Anthropic unterstützt die meisten JSON-Schemafunktionen:
use adk_model::anthropic::AnthropicSchemaAdapter;
let adapter = AnthropicSchemaAdapter;
GenericSchemaAdapter
Standard für unbekannte Anbieter (Ollama, DeepSeek usw.):
use adk_core::GenericSchemaAdapter;
let adapter = GenericSchemaAdapter;
Schema-Caching
Normalisierte Schemas werden anhand eines Inhalts-Hashes zwischengespeichert, um redundante Berechnungen zu vermeiden:
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();
Jeder Cache besitzt eine eigene Adapterinstanz, sodass für verschiedene Anbieter oder Adapterkonfigurationen normalisierte Einträge nicht kollidieren können. Anbieter-Clients verwenden diese Caches automatisch während generate_content().
Kürzen von Toolnamen
Alle Adapter kürzen Toolnamen, die 64 Bytes überschreiten, an gültigen UTF-8-Zeichen-Grenzen:
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());
Gemeinsame Dienstprogramme
Das Modul adk_core::schema_utils stellt zusammensetzbare Transformationsfunktionen bereit, die Adapter intern verwenden:
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"]);
Verfügbare Dienstprogramme:
strip_schema_keyword— entfernt$schemastrip_conditional_keywords— entferntif/then/elseadd_implicit_object_type— fügttype: "object"hinzu, wennpropertiesvorhanden istconvert_const_to_enum— konvertiertconstin einenummit einem Elementstrip_unsupported_formats— entfernt Formatwerte, die nicht in der Zulassungsliste enthalten sindstrip_null_from_enum— entfernt null aus Enum-Arraystruncate_tool_name— kürzt an der Grenze UTF-8resolve_refs— bettet$ref-Referenzen aus Definitionen eincollapse_combiners— reduziertanyOf/oneOfauf den ersten Nicht-null-Wertmerge_all_of— führtallOf-Unterschemas zusammencollapse_type_arrays— reduziert["string", "null"]auf"string"enforce_nesting_depth— ersetzt tiefe Schemas durch{"type": "object"}
Benutzerdefinierte Adapter
Implementieren Sie SchemaAdapter für benutzerdefinierte Anbieter:
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
}
}
Beispiel
Führen Sie die Demo zur Schemanormalisierung aus, um alle Adapter in Aktion zu sehen:
cd examples/schema_normalization
cargo run
Es werden keine API-Schlüssel benötigt — die Normalisierungslogik wird lokal demonstriert.
Migration von sanitize_schema
Wenn Sie sich zuvor darauf verlassen haben, dass McpToolset vorab bereinigte Schemas zurückgibt:
- Für die Standardverwendung sind keine Codeänderungen erforderlich. Das
Toolset-Trait API ist unverändert. - Schemas werden jetzt zum Zeitpunkt der Anfrage durch den Modelladapter normalisiert, nicht bei der Toolregistrierung.
- Wenn Sie
parameters_schema()aufgerufen und eine Gemini-formatierte Ausgabe erwartet haben, wird jetzt stattdessen das unformatierte Schema zurückgegeben. Verwenden Sie bei Bedarf das entsprechendeSchemaAdapter, um es selbst zu normalisieren.
Zurück: ← MCP Tools | Weiter: Sitzungen →