Agent Output Verifier

Verifica de forma independiente la salida de un agente antes de pagar: estructura, formatos, rangos y reglas entre campos, con una atestación firmada y un recibo. 3 llamadas gratuitas al día, luego x402 v2.

Servidor MCP alojado

npx add-mcp 'https://fastapi-service-5ag4.onrender.com/mcp'

Se instala en Claude Code, Codex, Cursor y más

Documentación

Agent Output Verifier

Verifica de forma independiente la salida de un agente antes de pagar.

Comprobaciones independientes y deterministas para facturas, entregables y cualquier salida estructurada: estructura, formatos, rangos y reglas entre campos, como "los totales de línea deben ser iguales al total", según tus propios requisitos, escritos en JSON Schema estándar más reglas opcionales. Devuelve aprobado/rechazado, el porcentaje de comprobaciones superadas, sugerencias de corrección y una atestación firmada con Ed25519 y un recibo que cualquiera puede verificar con nuestra clave pública. Vendedores: comprueben su propia salida antes de enviarla y entréguenla con una atestación firmada y un recibo. Sin registro: pago por llamada con x402 v2, en USDC en Base o Solana. 3 llamadas gratuitas al día a través de MCP.

Este repositorio es documentación y metadatos públicos para el servicio alojado: no hay código fuente que instalar ni ejecutar aquí. El servicio en sí es una API en vivo en https://fastapi-service-5ag4.onrender.com.

Endpoints

PropósitoEndpoint
MCP (HTTP Streamable)https://fastapi-service-5ag4.onrender.com/mcp
Verificar una salida (REST)POST https://fastapi-service-5ag4.onrender.com/verify/schema
Consultar la puntuación de confianza de un agente (REST)GET https://fastapi-service-5ag4.onrender.com/score/{agent_id}
Tarjeta de agente (descubrimiento)GET https://fastapi-service-5ag4.onrender.com/.well-known/agent-card.json
Manifiesto de descubrimiento x402GET https://fastapi-service-5ag4.onrender.com/.well-known/x402
llms.txtGET https://fastapi-service-5ag4.onrender.com/llms.txt
Documentación interactiva de la APIGET https://fastapi-service-5ag4.onrender.com/docs

Se exponen dos herramientas MCP en el mismo endpoint: verify_schema (POST /verify/schema) y get_verification_record (GET /score/{agent_id}).

Precios y redes

Pago por llamada con x402 v2, en USDC, en cualquiera de las redes siguientes: sin cuenta ni registro:

RutaPrecioRedes
POST /verify/schema$0.02 USDCBase (eip155:8453), Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp)
GET /score/{agent_id}$0.01 USDCBase (eip155:8453), Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp)

/.well-known/x402 y el encabezado Payment-Required de una respuesta 402 en vivo siempre llevan los precios exactos en vivo, las direcciones de pago y las direcciones de los activos: trátelos como la fuente de verdad y esta tabla como referencia rápida.

Tres encabezados transportan el pago en sí, por nombre: una solicitud no pagada recibe HTTP 402 con el precio y los requisitos de pago en el encabezado de respuesta Payment-Required; una solicitud pagada lleva su pago firmado en el encabezado de solicitud Payment-Signature; una respuesta liquidada lleva el recibo de liquidación en el encabezado de respuesta Payment-Response. Un resultado fail es una respuesta completa y válida a la pregunta formulada y se cobra igual que un pass: el trabajo (ejecutar todas las comprobaciones) es idéntico en ambos casos.

Prueba gratuita

Cada herramienta MCP tiene su propia asignación de prueba gratuita: 3 llamadas gratuitas por día por cliente, identificadas por la dirección IP de la solicitud; no hay registro ni cuenta, disponible a través de MCP. Las llamadas REST y MCP desde la misma dirección comparten una asignación por herramienta.

El cuerpo 402 de una llamada REST no pagada informa del estado real de este llamante, en vivo: available, remaining_calls_today y resets_at (la próxima medianoche UTC). Una vez que se agotan las llamadas del día de un cliente, available pasa a false y el cuerpo lleva "code": "free_trial_exhausted", distinto de una respuesta ordinaria de pago requerido que nunca tuvo prueba gratuita:

{
  "free_trial": {
    "available": false,
    "via": "mcp",
    "transport": "streamable-http",
    "url": "https://fastapi-service-5ag4.onrender.com/mcp",
    "tool": "verify_schema",
    "calls_per_client_per_day": 3,
    "remaining_calls_today": 0,
    "resets_at": "2026-10-02T00:00:00+00:00",
    "client": "identified by request IP address",
    "message": "This client's 3 free calls/day for the MCP tool 'verify_schema' are used up for today; they reset at 2026-10-02T00:00:00+00:00. This REST endpoint has no free tier of its own.",
    "code": "free_trial_exhausted"
  }
}

El mismo código free_trial_exhausted acompaña al resultado de pago requerido de la propia herramienta MCP (en _meta) cuando se realiza una llamada a la herramienta después de que se agoten las llamadas gratuitas del día de ese cliente. Después de la prueba gratuita (o una vez agotada para el día), una llamada requiere un pago x402 transportado en el _meta de la solicitud MCP bajo x402/payment, o un encabezado de pago x402 estándar en REST.

Verificación de un recibo, paso a paso

Cada respuesta de /verify/schema y /score/{agent_id} lleva una sección verify firmada y un bloque attestation, por ejemplo:

"verify": {
  "public_key_url": "https://fastapi-service-5ag4.onrender.com/.well-known/agent-card.json",
  "key_version": "v1-2026-09c",
  "canonicalization": "RFC 8785 (JCS)",
  "hash_algorithm": "SHA-256",
  "service": "Agent Output Verifier",
  "mcp": "https://fastapi-service-5ag4.onrender.com/mcp"
},
"attestation": {
  "algorithm": "Ed25519",
  "key_version": "v1-2026-09c",
  "timestamp": "2026-10-01T01:01:09.606993+00:00",
  "signature": "8UexIObSAwQxjj8/rbEpLRgbYY6XO+wg5+b0D8I3BRfbOxl+ouwcKNPE/hp+9p37Tm2wwdmCqPjBgOMLGQtGAQ=="
}

No se necesita nada más que la propia respuesta y verify.public_key_url:

  1. Obtenga la clave pública. GET la tarjeta de agente en verify.public_key_url. Su matriz capabilities.extensions tiene una entrada cuyo uri comienza con urn:json-schema-verifier:extension:attestation:; su params.publicKeys enumera todas las claves con las que el servicio ha firmado alguna vez, cada una {keyVersion, algorithm, publicKey} (la clave pública Ed25519 cruda de 32 bytes, codificada en base64). Elija la entrada cuyo keyVersion sea igual a response.attestation.key_version. La tarjeta de agente en vivo es siempre la fuente de verdad; esta es una copia de conveniencia de la clave actual, que vale la pena reconfirmar contra la tarjeta de agente en vivo primero:

    {
      "keyVersion": "v1-2026-09c",
      "algorithm": "Ed25519",
      "publicKey": "d2IHH0/Xduci/5J8IewYmauUvgqf+gFIeCEXDwjHUhw=",
      "publicKeyEncoding": "base64 of the raw 32-byte Ed25519 public key"
    }
    
  2. Reconstruya el documento firmado. Es un objeto JSON con exactamente estas cinco claves:

    {
      "context": "json-schema-verifier/verify-schema-attestation/v1",
      "algorithm": "Ed25519",
      "key_version": "<attestation.key_version>",
      "attested_at": "<attestation.timestamp>",
      "response": { "...": "the full response, with the attestation field removed" }
    }
    

    context es json-schema-verifier/verify-schema-attestation/v1 para respuestas /verify/schema, o json-schema-verifier/trust-score-attestation/v1 para respuestas /score/{agent_id}.

  3. Canonicalícelo con RFC 8785 (JCS): bytes UTF-8 del documento JSON anterior, claves de objeto ordenadas recursivamente, sin espacios en blanco insignificantes, números impresos como los imprime ECMAScript (1.0 se convierte en 1).

  4. Verifique la firma Ed25519 (response.attestation.signature, decodificada en base64) sobre esos bytes canónicos, usando la clave pública del paso 1.

  5. Compruebe los cuatro hash del recibo, cada uno SHA-256 sobre el JSON canónico RFC 8785 (JCS) del valor nombrado: output_hash (de submitted_output), schema_hash (de expected_schema), rules_hash (de {"bounds": ..., "rules": ...}) y request_hash (de toda la solicitud tal como se analizó, solo detalle completo). Recálculelos usted mismo y compárelos: esto es lo que vincula el recibo a los datos exactos que se comprobaron. request_hash rellena cada campo por regla y por límite con su propio valor predeterminado antes de aplicar el hash: un tolerance omitido se procesa como 1e-9, un if_present omitido como false, un equals_field/equals/other_field/value/in_field/ values omitido como null —, de modo que dos solicitudes cuyas reglas difieren solo en qué campos opcionales se escribieron explícitamente aún tengan el mismo hash aquí. rules_hash es lo contrario: aplica hash exactamente y solo a los campos que usted proporcionó, sin valores predeterminados.

Un verificador mínimo en Python, que usa solo la biblioteca estándar más cryptography:

import base64, json
from decimal import Decimal
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

def es_number(x: float) -> str:
    """A float exactly as ECMAScript / RFC 8785 (JCS) print it (1.0 -> "1", 1e-7 -> "1e-7")."""
    if x == 0:
        return "0"
    sign = "-" if x < 0 else ""
    digits_tuple = Decimal(repr(abs(x))).as_tuple()
    digits = "".join(map(str, digits_tuple.digits)).rstrip("0") or "0"
    n = len(digits_tuple.digits) + digits_tuple.exponent  # position of the decimal point
    k = len(digits)
    if k <= n <= 21:
        body = digits + "0" * (n - k)
    elif 0 < n <= 21:
        body = digits[:n] + "." + digits[n:]
    elif -6 < n <= 0:
        body = "0." + "0" * (-n) + digits
    else:
        e = n - 1
        body = digits[0] + ("." + digits[1:] if k > 1 else "") + f"e{'+' if e >= 0 else '-'}{abs(e)}"
    return sign + body

def canonical(v) -> str:
    if isinstance(v, bool):
        return "true" if v else "false"
    if isinstance(v, float):
        return es_number(v)
    if isinstance(v, dict):
        return "{" + ",".join(f"{canonical(k)}:{canonical(v[k])}" for k in sorted(v)) + "}"
    if isinstance(v, list):
        return "[" + ",".join(canonical(x) for x in v) + "]"
    return json.dumps(v, ensure_ascii=False)  # covers None, int, str

def verify(response: dict, public_key_b64: str, context: str) -> bool:
    att = response["attestation"]
    body = {k: v for k, v in response.items() if k != "attestation"}
    message = canonical({
        "context": context,
        "algorithm": att["algorithm"],
        "key_version": att["key_version"],
        "attested_at": att["timestamp"],
        "response": body,
    }).encode("utf-8")
    key = Ed25519PublicKey.from_public_bytes(base64.b64decode(public_key_b64))
    try:
        key.verify(base64.b64decode(att["signature"]), message)
        return True
    except Exception:
        return False

Probado contra ambos recibos del ejemplo práctico a continuación: ambos verifican True.

Ejemplo práctico: verificación de facturas

Ambas llamadas siguientes son llamadas reales, en vivo y pagadas contra el servicio de producción: pagos x402 reales en Base, liquidados en cadena, con los recibos firmados resultantes mostrados exactamente como se recibieron. Ambos son verificables de forma independiente: obtenga la tarjeta de agente anterior, confirme que key_version: v1-2026-09c está listado y verifique cada attestation.signature con los pasos anteriores.

1. Factura borrador: falla en una regla entre campos (detail: "full")

Solicitud:

{
  "task_id": "invoice-7731-draft",
  "agent_id": "test-seller-invoice-agent",
  "expected_schema": {
    "type": "object",
    "required": ["invoice_id", "currency", "line_items", "total"],
    "properties": {
      "invoice_id": { "type": "string", "pattern": "^INV-[0-9]{4}$" },
      "currency": { "enum": ["USD", "EUR"] },
      "line_items": {
        "type": "array",
        "minItems": 1,
        "items": {
          "type": "object",
          "required": ["sku", "qty", "line_total"],
          "properties": {
            "sku": { "type": "string" },
            "qty": { "type": "integer", "minimum": 1 },
            "line_total": { "type": "number", "minimum": 0 }
          }
        }
      },
      "total": { "type": "number" }
    }
  },
  "submitted_output": {
    "invoice_id": "INV-7731",
    "currency": "USD",
    "line_items": [
      { "sku": "CRWL-100", "qty": 3, "line_total": 87.0 },
      { "sku": "CRWL-200", "qty": 1, "line_total": 42.5 }
    ],
    "total": 135.0
  },
  "rules": [
    { "type": "sum_equals", "field": "line_items[].line_total", "equals_field": "total" }
  ],
  "enforce_rules": true,
  "detail": "full"
}

Las líneas suman 129.5, pero total dice 135.0. Con enforce_rules: true, esa violación de regla hace fallar el resultado y viene con una sugerencia de corrección:

{
  "summary": {
    "result": "fail",
    "blocking_checks": ["consistency:sum_equals"]
  },
  "next_actions": [
    { "action": "repair_and_reverify", "using": "hints" }
  ],
  "result": "fail",
  "verification_id": "28afb453-7c39-4993-a9c4-fcade1b7924f",
  "task_id": "invoice-7731-draft",
  "agent_id": "test-seller-invoice-agent",
  "output_hash": "d4fb804e434eb3ee047068cbb3691ea1e07066928e7e3efc73b94317e53ee305",
  "schema_hash": "23ea3213715cfaf2011dabe2a9a69d4ac25b6eceec7ce3d4bfef9a1298b69b2a",
  "rules_hash": "2acb6de92b88cfbca279ea6d5bd74b7a1e7dec0839dd093dbe041e25df3364d2",
  "request_hash": "e4bf9733726b24a19ec546ecc79015c9b1cb7a6829b409b41ef56b1c8f60b881",
  "verified_at": "2026-10-01T01:01:09.606993+00:00",
  "verifier_version": "0.4.1",
  "enforce_rules": true,
  "strict_content_check": false,
  "errors": [
    "enforce_rules: consistency:sum_equals at 'line_items[].line_total': values sum to 129.5, expected total (135.0)"
  ],
  "errors_total": 1,
  "errors_omitted": 0,
  "errors_detail": [
    {
      "check": "consistency:sum_equals",
      "path": "$.line_items[].line_total",
      "expected": "a sum equal to total (135.0)",
      "found": "sum 129.5",
      "message": "enforce_rules: consistency:sum_equals at 'line_items[].line_total': values sum to 129.5, expected total (135.0)"
    }
  ],
  "errors_detail_total": 1,
  "errors_detail_omitted": 0,
  "flags": [
    {
      "check": "consistency:sum_equals",
      "field": "line_items[].line_total",
      "severity": "error",
      "detail": "values sum to 129.5, expected total (135.0)"
    }
  ],
  "flags_total": 1,
  "flags_omitted": 0,
  "hints": [
    {
      "source": "integrity",
      "check": "consistency:sum_equals",
      "path": "$.line_items[].line_total",
      "expected": "a sum equal to total (135.0)",
      "found": "sum 129.5"
    }
  ],
  "hints_total": 1,
  "hints_omitted": 0,
  "schema_compliance": { "ratio": 1.0, "passed": 22, "total": 22 },
  "integrity_checks": { "ratio": 0.875, "passed": 7, "total": 8 },
  "overall": { "ratio": 0.966667, "passed": 29, "total": 30 },
  "verify": {
    "public_key_url": "https://fastapi-service-5ag4.onrender.com/.well-known/agent-card.json",
    "key_version": "v1-2026-09c",
    "canonicalization": "RFC 8785 (JCS)",
    "hash_algorithm": "SHA-256",
    "service": "Agent Output Verifier",
    "mcp": "https://fastapi-service-5ag4.onrender.com/mcp"
  },
  "attestation": {
    "algorithm": "Ed25519",
    "key_version": "v1-2026-09c",
    "timestamp": "2026-10-01T01:01:09.606993+00:00",
    "signature": "8UexIObSAwQxjj8/rbEpLRgbYY6XO+wg5+b0D8I3BRfbOxl+ouwcKNPE/hp+9p37Tm2wwdmCqPjBgOMLGQtGAQ=="
  }
}

Evidencia de pago: $0.02 USDC en Base, liquidado en cadena: 0x4cb755c71413ed5419215d2d7445c34e19be79d9f15b4046802656165b774897.

2. Factura corregida: pasa (detail: "compact")

Mismo esquema y regla, total corregido para coincidir con las líneas (129.5), solicitado con "detail": "compact" para obtener solo el resumen, los campos del recibo y la firma:

{
  "task_id": "invoice-7731-final",
  "agent_id": "test-seller-invoice-agent",
  "expected_schema": { "...": "same schema as above" },
  "submitted_output": {
    "invoice_id": "INV-7731",
    "currency": "USD",
    "line_items": [
      { "sku": "CRWL-100", "qty": 3, "line_total": 87.0 },
      { "sku": "CRWL-200", "qty": 1, "line_total": 42.5 }
    ],
    "total": 129.5
  },
  "rules": [
    { "type": "sum_equals", "field": "line_items[].line_total", "equals_field": "total" }
  ],
  "enforce_rules": true,
  "detail": "compact"
}
{
  "summary": {
    "result": "pass",
    "blocking_checks": []
  },
  "next_actions": [
    { "action": "attach_receipt", "for": "seller" },
    { "action": "proceed_to_payment", "for": "buyer" }
  ],
  "result": "pass",
  "verification_id": "96b4f722-04c3-47c7-8708-5bba97fd18b5",
  "task_id": "invoice-7731-final",
  "agent_id": "test-seller-invoice-agent",
  "output_hash": "39e3d0a1431e3ce9cda89a1d2873e1ffb4e34f4eeded2ffa87946fbd941a34c3",
  "schema_hash": "23ea3213715cfaf2011dabe2a9a69d4ac25b6eceec7ce3d4bfef9a1298b69b2a",
  "rules_hash": "2acb6de92b88cfbca279ea6d5bd74b7a1e7dec0839dd093dbe041e25df3364d2",
  "verified_at": "2026-10-01T01:01:15.158857+00:00",
  "verifier_version": "0.4.1",
  "enforce_rules": true,
  "strict_content_check": false,
  "verify": {
    "public_key_url": "https://fastapi-service-5ag4.onrender.com/.well-known/agent-card.json",
    "key_version": "v1-2026-09c",
    "canonicalization": "RFC 8785 (JCS)",
    "hash_algorithm": "SHA-256",
    "service": "Agent Output Verifier",
    "mcp": "https://fastapi-service-5ag4.onrender.com/mcp"
  },
  "attestation": {
    "algorithm": "Ed25519",
    "key_version": "v1-2026-09c",
    "timestamp": "2026-10-01T01:01:15.158857+00:00",
    "signature": "ri1AEl12DoZMTKBX8V5hfuD9jOWd+BLXIciqiX1JoSHJy8iDJKKSdsJmjpQUHTkbVMxDGpNwMNmc6X9zHaSVDw=="
  }
}

La forma compacta conserva el resumen, los campos del recibo, verify y attestation; request_hash, errors, errors_detail, flags, hints y las tres puntuaciones permanecen en la forma completa (consulte detail anterior).

Evidencia de pago: $0.02 USDC en Base, liquidado en cadena: 0xaa9161736ac410b6ac9ccc478780aa2feaa6d2cf02af4c51855e099c14499079.

Verificación de estos dos recibos por su cuenta

Ambas respuestas anteriores se comprobaron contra la clave pública de la tarjeta de agente en vivo para v1-2026-09c antes de incluirlas aquí, usando los pasos de Verificación de un recibo. Confírmelo usted mismo de la misma manera: obtenga https://fastapi-service-5ag4.onrender.com/.well-known/agent-card.json, confirme que v1-2026-09c está listado y vuelva a ejecutar los mismos cinco pasos contra los dos bloques JSON anteriores: las firmas se calcularon una vez, sobre exactamente estos datos, y se verificarán de manera idéntica para cualquiera.

agent_id identifica al agente que produjo la salida que se está comprobando, por ejemplo, el vendedor; es una etiqueta no autenticada que proporciona el llamante. agent_id: "test-seller-invoice-agent" usa el prefijo test- que el servicio reserva para ejemplos: la verificación se ejecutó de verdad y su recibo es genuino, manteniéndose separado de la puntuación de confianza de ese agente.

Licencia

La documentación y los ejemplos de este repositorio están bajo la Licencia MIT. El servicio alojado es propietario; este repositorio contiene solo documentación y metadatos públicos.