← Blog
4 de octubre, 2026 · 19 min

Un schema, cuatro dialectos: auditamos los structured outputs

Un agente que envía strict: true cree que compró una garantía: la respuesta va a parsear y va a cumplir el schema. Seguimos ese flag por tres providers, cinco capas de traducción, un modelo local y nuestra propia cotización x402. Se sostiene en menos saltos de lo que su nombre sugiere, y cada salto donde se rompe igual devuelve HTTP 200.

Los agentes funcionan con JSON. Argumentos de tools, resultados de extracción, decisiones de routing e intenciones de pago salen del modelo como un string que otro programa tiene que parsear. Desde hace dos años, la respuesta a "¿y si no parsea?" es el constrained decoding. El provider compila tu JSON Schema en una gramática y enmascara cada token que la rompería. OpenAI llama a la función Structured Outputs. Anthropic ofrece JSON outputs y strict tool use. Gemini, vLLM y Ollama tienen cada uno su propia versión.

El titular es el mismo en todas partes: el output va a cumplir tu schema. La letra chica no. Cada provider acepta un subconjunto distinto de JSON Schema. Cada capa de compatibilidad trata el flag strict de forma diferente. Algunos motores descartan keywords sin avisar. Y una respuesta válida según el schema no es lo mismo que una respuesta verdadera.

Nuestras fuentes, todas leídas o ejecutadas el 4 de octubre de 2026: las guías de structured outputs y function calling de OpenAI; la página de structured outputs de Anthropic y su página de compatibilidad con el SDK de OpenAI; los docs de structured output y compatibilidad con OpenAI de Gemini; los docs de structured outputs, response healing y provider routing de OpenRouter; vLLM en el commit 0872ddf, Ollama en 42e911b y LiteLLM en bb4f721; la especificación MCP 2026-07-28; y la extensión Bazaar de x402 en 751590a. También corrimos una prueba de enforcement de 16 keywords contra Ollama 0.30.6 y sondeamos nuestro propio 402 walk-up.

De dónde vienen los schemas de los agentes

Los schemas de los agentes rara vez se escriben a mano para un solo modelo. Llegan a través de protocolos.

MCP es la fuente más grande. En la especificación de tools 2026-07-28, cada tool lleva un inputSchema y puede llevar un outputSchema. Ambos usan JSON Schema 2020-12 por defecto cuando no hay $schema. Si existe un output schema, "los servidores DEBEN entregar resultados estructurados que cumplan este schema" y "los clientes DEBERÍAN validar los resultados estructurados contra este schema". Para una tool sin parámetros, la spec recomienda { "type": "object", "additionalProperties": false }.

La misma página agrega una nota que vale la pena leer dos veces. structuredContent "son datos de resultado producidos por el servidor y no tienen relación con los 'structured outputs' de los LLM (generación del modelo restringida por schema)". MCP restringe lo que devuelve el servidor. No dice nada sobre lo que emite el modelo cuando completa los argumentos de la tool. Esa mitad le corresponde al provider y a cualquier gateway que esté en medio.

La extensión Bazaar de x402 es la segunda fuente. Un resource server declara su endpoint HTTP o su tool MCP para que los facilitators lo cataloguen. Para tools MCP, inputSchema es obligatorio, sigue el formato Tool.inputSchema de MCP, y los servidores "deberían reutilizar el mismo schema que su tool MCP ya declara". El objeto info.output, en cambio, tiene un type, un format opcional y un example opcional. No hay campo de output schema. Un agente que encuentra una API paga a través del Bazaar obtiene un contrato para lo que envía y una muestra de lo que vuelve.

El propio ejemplo GET del Bazaar declara además headers como un objeto con additionalProperties: { "type": "string" }. Guarda ese detalle. Como muestra la siguiente sección, dos de los tres providers principales rechazan esa forma en modo strict.

Cubrimos el discovery en el post del Bazaar y la revisión del protocolo en el post de MCP 2026-07-28. El punto aquí es más acotado. Los schemas que un agente le pasa a un modelo se escribieron para validar, en JSON Schema 2020-12 completo. El constrained decoding habla un dialecto, y cada provider habla uno distinto.

Lo que strict promete en realidad

OpenAI. Structured Outputs "garantiza que el modelo siempre genere respuestas que se ajusten al JSON Schema que proporcionaste". El precio de esa frase es un conjunto de reglas. La raíz debe ser un objeto, no un anyOf, así que una discriminated union en el nivel superior falla; la guía de OpenAI usa Zod como ejemplo del patrón. Cada campo debe estar en required; los campos opcionales se emulan con una unión que incluye null. additionalProperties: false "siempre debe estar definido en los objetos". Un schema puede tener hasta 5.000 propiedades de objeto, 10 niveles de anidamiento, 1.000 valores de enum y 120.000 caracteres entre nombres de propiedades, nombres de definiciones y valores de enum y const.

Dentro de esas reglas, OpenAI soporta mucho: pattern, nueve valores de format para strings, minimum, maximum, los límites exclusivos, multipleOf, minItems, maxItems, anyOf, definiciones y schemas recursivos. No soporta allOf, not, dependentRequired, dependentSchemas ni if/then/else. Con strict: true, un schema no soportado devuelve un error en lugar de una respuesta degradada. El primer request con un schema nuevo agrega latencia mientras se procesa el schema.

El default depende del endpoint. Para function tools, los requests de Chat Completions "siguen siendo no estrictos por defecto". Los requests de Responses "intentarán normalizar tu schema a modo strict cuando sea posible" y caen a function calling best-effort cuando no pueden, reportando strict: false en la tool. La misma definición de tool obtiene una garantía en un endpoint y un mejor esfuerzo en el otro.

Quedan dos casos borde. Un rechazo por seguridad vuelve en un campo refusal separado, porque "no necesariamente sigue el schema". Llegar al límite de tokens deja la respuesta incompleta.

Anthropic. Los JSON outputs pasaron a output_config.format y están en disponibilidad general. El parámetro beta output_format está deprecated. Strict tool use es strict: true en una tool. Ambos comparten un mismo conjunto de límites.

Soportado: los tipos básicos, enum de primitivos, const, anyOf, allOf salvo con $ref, $ref locales y definiciones, default, diez formatos de string incluido uri, y minItems de 0 o 1. additionalProperties debe ser false. No soportado: schemas recursivos, restricciones numéricas como minimum, maximum y multipleOf, las restricciones de string minLength y maxLength, y cualquier restricción de array más allá de minItems de 0 o 1. "Si usas una función no soportada, recibirás un error 400 con detalles". El soporte de regex excluye backreferences, lookaround y word boundaries.

Además hay límites de complejidad por request: 20 strict tools, 24 parámetros opcionales sumando todos los schemas strict, y 16 parámetros con union types, incluido ["string", "null"]. Pasados esos límites, o los límites internos de tamaño de gramática, la API responde "Schema is too complex for compilation". El truco de la unión nullable que OpenAI recomienda para campos opcionales consume el presupuesto de uniones de Anthropic.

Los costos también están documentados. Las gramáticas se compilan en el primer uso y quedan en caché 24 horas desde el último uso. Claude "recibe automáticamente un system prompt adicional que explica el formato de output esperado", lo que sube los input tokens, y cambiar output_config.format invalida el prompt cache de esa conversación. Las propiedades requeridas se emiten primero y las opcionales después, así que el orden de las keys puede diferir del schema.

Y la garantía tiene huecos documentados. Un refusal devuelve HTTP 200 con stop_reason: "refusal", "se te cobrarán los tokens generados", y el output puede no cumplir el schema. Un corte por max_tokens puede dejar el output incompleto. Lo más inusual: los valores de enum y const de tipo string no tienen garantizada su capitalización. Claude puede devolver "Conversation Topic 3" para un valor de enum con t minúscula, y "la respuesta se completa normalmente, sin error y sin un stop_reason especial".

Gemini. Google documenta una lista más corta. Los objetos aceptan properties, required y additionalProperties, que "puede ser un boolean o un schema". Los strings aceptan enum y format, "como date-time, date, time". Los números aceptan enum, minimum y maximum. Los arrays aceptan items, prefixItems, minItems y maxItems. Los propios ejemplos de la página usan anyOf y un organigrama recursivo con "$ref": "#". La sección de limitaciones dice "no todas las funciones de JSON Schema están soportadas" y "los schemas muy grandes o muy anidados pueden ser rechazados". No dice si un keyword no listado da error o se ignora. Sus buenas prácticas te piden "siempre validar los valores en tu aplicación".

# Soporte de keywords para output restringido, según los docs de cada provider, 4 oct 2026
# "-" = no figura en la lista documentada del provider
keyword                       OpenAI strict       Anthropic            Gemini
toda propiedad en required    MUST                no                   no
additionalProperties          false, todo obj     solo false           bool o schema
anyOf                         sí, no en raíz      sí                   sí (ejemplo)
allOf                         error               sí, sin $ref         -
$ref recursivo                sí                  400                  sí (ejemplo)
minimum / maximum             sí                  400                  sí
multipleOf                    sí                  400                  -
minLength / maxLength         -                   400                  -
pattern                       sí                  solo regex simple    -
format                        9 valores           10 valores (+ uri)   date-time, date, time...
minItems / maxItems           sí                  minItems 0 o 1       sí
uniqueItems                   -                   400                  -

Ahora pasa schemas reales por esa tabla. El propio organigrama recursivo de Gemini no define additionalProperties, así que el modo strict de OpenAI lo rechaza, y es recursivo, así que Anthropic también lo rechaza. Un entero acotado por minimum: 1 y maximum: 10 se aplica en OpenAI y Gemini y es un 400 en Anthropic. El mapa headers del Bazaar falla tanto en OpenAI strict como en Anthropic. Ningún schema no trivial es portable por defecto. La portabilidad es algo que una capa tiene que hacer a propósito.

Cinco capas, cinco significados de strict

La mayoría de los agentes no llama a estas APIs de forma nativa. Llama a un endpoint con forma de OpenAI y deja que algo traduzca. Leímos qué hace cada capa de traducción con response_format: { type: "json_schema", json_schema: { strict: true, ... } }.

El endpoint compatible con OpenAI de Anthropic lista response_format como "Ignored" y remite a los Structured Outputs nativos. El strict a nivel de función también se ignora, "lo que significa que el JSON del tool use no tiene garantizado seguir el schema provisto". Ambos devuelven igual una respuesta normal.

La compatibilidad con OpenAI de Gemini documenta structured output a través del helper parse() del SDK de OpenAI, con ejemplos en Pydantic y Zod. Su sección "Current limitations" dice que el soporte para las librerías de OpenAI "todavía está en beta".

Ollama decodifica json_schema en un struct con exactamente un campo, Schema, en openai/openai.go. Los campos name y strict nunca pasan del decoder. El schema pelado se convierte en el parámetro nativo format, así que el schema siempre se aplica y el flag no significa nada. Los docs agregan dos líneas: "Ollama's Cloud actualmente no soporta structured outputs", y es "ideal pasar también el JSON schema como string en el prompt".

vLLM también ignora el flag, en la dirección contraria. structured_outputs_from_response_format convierte cualquier json_schema en un request restringido, tenga o no strict. El backend por defecto es auto: probar xgrammar y, si el schema falla la validación o usa funciones que xgrammar no soporta, caer a guidance por defecto. La lista de no soportados incluye multipleOf, uniqueItems, contains y formatos de string fuera de un conjunto soportado. Un comentario del código es inusualmente franco. Cuando un string combina pattern o format con límites de longitud, xgrammar "descarta en silencio minLength/maxLength de la gramática, así que el output puede violar el límite sin que aparezca ningún error". vLLM detecta ese caso y lo redirige. Un motor sin ese chequeo no lo haría.

LiteLLM, al enrutar a un modelo Claude con soporte nativo, mapea response_format al formato de Anthropic y ejecuta filter_anthropic_output_schema. Elimina restricciones de array, numéricas, de longitud de string y condicionales, reescribe oneOf como anyOf y agrega cada restricción eliminada a la descripción del campo. El docstring dice que esto "replica la transformación que hace el SDK de Python de Anthropic". El SDK de Anthropic luego valida la respuesta contra el schema original. El switch equivalente de LiteLLM, enable_json_schema_validation, viene en False por defecto. Por defecto, una restricción eliminada se convierte en una pista. Para modelos Claude sin soporte nativo, LiteLLM envuelve el schema en una tool sintética y fuerza tool_choice hacia ella, salvo cuando thinking está activado o el modelo no soporta forced tool use; en ese caso la tool se ofrece pero no se fuerza.

OpenRouter dice que el soporte "se determina por endpoint, no solo por modelo". require_parameters viene en false, y los providers que no tienen un parámetro "ignorarán los parámetros desconocidos". response_format es una preferencia blanda entre providers del mismo modelo. Pero "esta preferencia nunca quita un modelo de la lista de candidatos de tu request (como la lista de fallback models)": si ningún provider de un modelo de fallback lo soporta, "el request igual se enruta a ese modelo y el parámetro se ignora". Incluso con strict: true, "el enforcement varía según el provider". Su plugin Response Healing repara sintaxis, como corchetes faltantes, comas finales y bloques markdown, solo en requests sin streaming, y "si la respuesta se truncó por max_tokens, el plugin no podrá repararla".

# Qué pasa con response_format json_schema + strict: true, por capa, 4 oct 2026
OpenAI nativo              se aplica; keyword no soportado -> error
Anthropic nativo           se aplica; keyword no soportado -> 400
Anthropic OpenAI-compat    response_format ignorado, strict ignorado
Ollama /v1                 name y strict descartados; schema siempre aplicado
vLLM                       schema siempre aplicado; strict nunca se lee
LiteLLM -> Claude          keywords no soportados movidos a descripciones;
                           output no se revalida por defecto
OpenRouter (defaults)      se enruta a providers que lo soportan si existen;
                           si no, el parámetro se ignora

Pon esa tabla al lado de una cadena de fallback. En nuestro post de fallback argumentamos que una cadena de dos modelos es la compra de confiabilidad más barata de un gateway. Con un schema adjunto, el segundo eslabón puede aplicarlo, rechazarlo o ignorarlo. El HTTP 200 se ve idéntico en los tres casos.

Lo que medimos: qué aplica realmente una gramática

Los docs listan keywords. No muestran qué pasa con los que dejan afuera. Así que medimos un motor directamente.

Setup: Ollama 0.30.6, qwen2.5:1.5b en CPU, el endpoint compatible con OpenAI, response_format con json_schema y strict: true, temperature 1.0, seis corridas por caso. Cada prompt le pedía al modelo romper la restricción, por ejemplo "Report the number 500" contra maximum: 10. Validamos cada output con jsonschema 4.20 de Python bajo Draft 2020-12 con un format checker. Un modelo chico fue deliberado: cuanto menos sigue un modelo el schema por su cuenta, más dice el resultado sobre la gramática.

# Ollama 0.30.6, qwen2.5:1.5b (CPU), 6 corridas por keyword, temperature 1.0, 4 oct 2026
# el prompt pide una violación; output validado con jsonschema (Draft 2020-12)
keyword                        válido         qué volvió
required                       6/6            emitió "b" aunque se pidió omitirlo
additionalProperties: false    6/6            los campos extra pedidos nunca aparecieron
enum ["red","green"]           6/6            "green" cuando se dijo que era blue
const "v2"                     6/6            "v2" cuando se dijo v9
minimum 1 / maximum 10         6/6            5 cuando se dijo 500
maxLength 8                    6/6            "The vast"
minLength 40                   5/6            texto de relleno; 1 corrida llegó a max_tokens a mitad del string
pattern ^[A-Z]{3}-[0-9]{4}$    6/6            "ABC-1234" cuando se dijo abc123
format: date                   6/6            "2023-11-27" cuando se dijo "next tuesday"
minItems 3 / maxItems 3        6/6            3 frutas cuando se pidieron 10
anyOf, dos ramas               6/6            una rama válida, valores inventados
$ref recursivo                 6/6            mantuvo "kids" cuando se pidió "children"
multipleOf 5                   0/6            7
format: email                  0/6            "Contact: John at the front desk."
uniqueItems (enum de 3)        0/6            ["apple", "apple", "apple"]
allOf [integer, minimum 100]   0/6            {} seguido de whitespace

Doce keywords se sostuvieron. Cuatro se descartaron: multipleOf, format: email, uniqueItems y allOf. Los 24 outputs fallidos volvieron como HTTP 200, sin error y sin advertencia. Nada en la respuesta le dijo al cliente que un tercio de sus restricciones no se había aplicado. La única forma de saberlo era validar.

El conjunto descartado importa para los agentes. multipleOf es la forma en que un schema dice que un monto se mueve en incrementos fijos. uniqueItems es la forma de decir que una lista de IDs no tiene duplicados. format: email protege un campo de contacto. allOf es el keyword de composición. En el caso de allOf, la gramática no solo se saltó el límite; el campo volvió como un objeto vacío, directamente del tipo equivocado.

Las filas que pasaron traen la segunda lección. Válido no es verdadero. Cuando se le dijo que el color era blue, el modelo devolvió green, porque green era legal y blue no. Cuando se le dijo 500, devolvió 5. Cuando se le dijo "next tuesday", inventó una fecha de calendario. Cuando se le pidió una palabra contra un mínimo de 40 caracteres, rellenó el string con texto sin relación, y una corrida nunca cerró el string antes de max_tokens. Sin gramática, un modelo puede decir "blue" o negarse. Bajo la gramática, todo output legal era incorrecto. El constrained decoding convierte "no puedo responder con esta forma" en una respuesta segura de sí misma con esta forma.

El truncamiento se comporta igual. Con el mismo setup y max_tokens: 8, la respuesta fue HTTP 200, finish_reason: "length", contenido { "summary": "The, y un bloque de usage que reportó 8 completion tokens. El string no parsea. Los tokens se contaron igual.

Regla de diseño — todo schema que un agente use para tomar una decisión necesita una forma legal de decir "ninguna de las anteriores": un campo nullable, un miembro "unknown" en el enum o una rama de error en un anyOf. Sin eso, la gramática fuerza una invención cada vez que la verdad queda fuera del schema. En Anthropic, cada unión nullable gasta uno de los 16 slots de unión, así que decide de antemano cuántos vas a usar.

Quién paga cuando el JSON está mal

Cada falla de este post devuelve HTTP 200. Un refusal en Anthropic se cobra por los tokens generados. Un objeto truncado se cobra por los tokens que usó. El healing no puede reparar el truncamiento. En una cuenta prepaga, eso son líneas en la factura. En un rail de pago por llamada, son pagos que el agente ya firmó por un string que su parser va a rechazar.

Sondeamos nuestra propia superficie walk-up igual que en el post de reasoning tokens. Un POST /v1/chat/completions sin autenticar devuelve HTTP 402 con un requisito exact en USDC sobre Base, y el header PAYMENT-REQUIRED lleva el monto. Cambiamos solo los campos relacionados con el schema.

# api.llm4agents.com, POST /v1/chat/completions sin autenticar, 4 oct 2026
# openai/gpt-5, max_tokens 500, un mensaje "hi" salvo que se indique     monto 402 (USDC, 6 decimales)
sin response_format                                             10000    $0.01
+ json_schema strict válido                                     10000    $0.01
+ schema con allOf y not (OpenAI strict rechaza ambos)          10000    $0.01
+ response_format {"type": "bogus"}                             10000    $0.01
+ body de 154 KB: schema strict, 400 propiedades x 50 enums     10000    $0.01
  mismo schema enviado como parameters de una tool              10000    $0.01
  mismo texto del schema pegado en el mensaje                   90000    $0.09
  150 KB de texto plano en el mensaje                           70000    $0.07
anthropic/claude-opus-5.5, schema con minimum/maximum           20000    $0.02
models [gpt-5, opus-5.5], en cualquier orden                    20000    $0.02

Tres lecturas.

Primero, la cotización no mira response_format. Un schema válido, un schema que el modo strict de OpenAI rechaza, un schema de 20.000 enums, veinte veces el límite de OpenAI, y un tipo de formato que no existe reciben el mismo 402. Lo mismo un request a Anthropic que usa minimum, que la API nativa rechaza y la capa de compatibilidad ignora. La cotización no puede distinguir un request que va a funcionar de uno que no.

Segundo, los bytes del schema son gratis en la cotización. El texto en el mensaje movió la cotización de $0.01 a $0.07 o $0.09. Los mismos bytes en response_format o en tools no la movieron en absoluto. Los upstreams cobran ambos. OpenAI dice que las definiciones de funciones "cuentan contra el límite de contexto del modelo y se cobran como input tokens". Anthropic dice que el prompt de formato inyectado "te cuesta tokens como cualquier otro system prompt". x402 no permite que un servidor cobre más de lo que el cliente firmó; bajo upto, el monto liquidado "DEBE ser menor o igual al máximo autorizado". Lo que un schema grande cueste por encima de la cotización es costo del gateway. En la auditoría del two-phase gap, una firma compraba muchas invocaciones. Aquí una firma barata compra un prompt caro.

Tercero, la lista de fallback se cotiza a su eslabón más caro, en cualquier orden. Esa es la decisión correcta bajo un scheme exact. Pero nada en la cotización ni en la respuesta le dice al agente si cada eslabón respetó el schema. Nuestra OpenAPI pública documenta model, models, messages, temperature, max_tokens y stream, con additionalProperties: true. response_format y tools pasan sin documentar. La única pista posterior es X-Model-Used, que nombra el modelo que respondió, no el dialecto que se aplicó.

Qué significa para LLM4Agents

Somos una capa de traducción, y cada fila de la tabla de cinco capas es un comportamiento que podríamos reproducir por accidente. Un gateway que acepta strict: true y lo reenvía a un backend que lo ignora vende una garantía que no entrega. El agente no puede ver la diferencia. Ve un 200 y un string JSON, y paga.

Nuestro fallback models también es un cambio de dialecto. Se activa por rate limits, caídas, largo de contexto y moderación. Cuando el primer eslabón aplica un schema y el segundo lo rechaza o lo ignora, el fallback o pierde la garantía o convierte un 429 transitorio en un 400 permanente. Confiabilidad y corrección tiran en direcciones opuestas salvo que el router conozca el dialecto de cada eslabón.

La cotización tiene un punto ciego justo donde crecen las cargas de los agentes. Los agentes con mucho MCP envían listas largas de tools y schemas grandes en cada llamada. Hoy esos bytes no le cuestan nada al agente al cotizar y nos cuestan input tokens reales upstream. Es un subsidio para los agentes honestos y una puerta para los abusivos.

La oportunidad es el mismo hecho dado vuelta. Los agentes no pueden seguir cuatro dialectos, cinco capas de traducción y los descartes silenciosos de cada motor. Un gateway que los conoce puede ser el único lugar donde un schema se revisa una vez, se traduce a propósito, se aplica donde es posible y se valida después, con el veredicto en el recibo. Eso es un producto, no un parche.

Cómo mantenerse en la frontera

En orden de costo y urgencia.

1. Cobrar lo que reenviamos. Contar response_format y tools en la estimación de input detrás del 402, igual que el texto del mensaje. Es el arreglo más barato y cierra el subsidio.

2. Hacer lint antes del 402. Revisar el schema contra el dialecto del modelo destino antes de cotizar. Rechazar con un 400 que nombre el keyword y el modelo, como minimum en un modelo Claude por una ruta que no puede aplicarlo. Aplicar los techos publicados: las 5.000 propiedades, 10 niveles y 1.000 valores de enum de OpenAI; las 20 strict tools, 24 parámetros opcionales y 16 parámetros de unión de Anthropic. Un agente nunca debería firmar por un request que ya sabemos que va a fallar.

3. Hacer el fallback consciente del schema. Cuando un request trae strict: true, mantener solo los eslabones de models que pueden aplicar ese schema, o traducirlo por eslabón como lo hace el SDK de Anthropic. En cualquier caso, decir cuál de las dos cosas pasó.

4. Validar después, siempre. Revisar cada output contra el schema original, no contra el traducido. Devolver el veredicto junto a X-Model-Used, por ejemplo como un header X-Schema-Valid más un modo: enforced, translated o validated-only. Los nombres son nuestros para elegir; lo que importa es la señal.

5. Normalizar las señales de falla. Mapear el stop_reason: "refusal" de Anthropic al campo refusal de OpenAI y todo corte por límite de tokens a finish_reason: "length", para que un solo parser maneje todos los eslabones. Nunca devolver un objeto truncado sin decirlo.

6. Ponerle precio a lo inválido. Bajo upto, el monto liquidado "PUEDE ser 0". Un gateway con su propio validador puede liquidar menos cuando el output no pasa. Definir la política para refusals, truncamientos y fallas de validación, publicarla y ponerla en el recibo. Hicimos el caso de la medición en el post de upto; este es el caso de la corrección.

7. Publicar el dialecto. Agregar response_format, tools y strict a la OpenAPI pública. Exponer el soporte de structured outputs por modelo en /api/v1/models, como OpenRouter expone los parámetros soportados. Después, lanzar un conversor de MCP a strict que transforme los parámetros opcionales en uniones nullable y ponga additionalProperties: false en todas partes, para que los schemas de tools de MCP y del Bazaar funcionen en modo strict sin editarlos a mano.

Los pasos uno y dos protegen el dinero. Del tres al cinco protegen la garantía. El seis y el siete la convierten en algo que un agente puede comprar a propósito.

Schemas que puedes verificar, por llamada

Un gateway compatible con OpenAI, pagado por llamada en USDC sobre x402.

Registra tu agente