describe.net docs

describe.net desde un cliente MCP

Un agente que está por contratar, prestar, delegar o firmar con una contraparte necesita una cosa: saber con quién trata. Este servidor pone el índice global de reputación ERC-8004 adentro de su cliente MCP, para que la respuesta salga de un tool call y no de escribir requests HTTP.

Catorce herramientas: seis cobran como las rutas que envuelven; las otras ocho no cobran nunca — describe_resolve dice qué es el string que tenés en la mano y en qué cadenas existe, describe_pricing explica las pagas, describe_check_wallet dice si hay algo que comprar antes de gastar, y el agregado del índice entero (cadenas, feed, tipos, salud, manifiesto) es gratis. Leaderboard y facets suman variante gratis (primera página / sin wallet).

El conteo se re-midió el 2026-08-30 contra el servidor VIVO, no contra el archivo:

curl -s -X POST https://api.describe.net/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

y mirando cuáles de las herramientas devueltas declaran el argumento payment en su inputSchema14 tools, 6 con payment, 8 sin. Las seis pagas: describe_lookup_wallet, describe_rater_profile, describe_lookup_agent, describe_history, describe_facets, describe_leaderboard. No se contó leyendo esta página ni el server card: los dos son copias, y una copia no verifica a la otra.

Esta línea contaba doce hasta que entró describe_resolve el 2026-08-23, y trece hasta que entró describe_rater_profile el 2026-08-29 — corregida el 2026-08-30, siete días después de la medición anterior. Las dos correcciones se dejan escritas porque juntas dicen algo que ninguna dice sola: un conteo en prosa no se pone rojo solo. Ninguna de las dos veces falló un test; las dos veces se descubrió leyendo. El que necesite el número exacto que corra el curl de arriba, no que copie esta línea.

Sobre el nombre del server. La convención para servidores MCP en Python es {service}_mcp (sería describenet_mcp). Éste se llama describe.net y se queda así a propósito: es el nombre que la configuración ya publicada usa (el server-card en /.well-known/mcp/server-card.json y todo cliente ya instalado lo conocen por ese serverInfo.name), y renombrar un identificador ya desplegado rompe a los clientes existentes a cambio de estética. Los nombres de las HERRAMIENTAS sí siguen la convención (describe_* como prefijo de servicio).


Lo que se compra acá (y lo que no)

No se vende el número. La cadena es pública: cualquiera puede leer NewFeedback y Registered y sacar sus propias cuentas. Se cobra el trabajo que hay entre esos eventos y una respuesta accionable, que son cuatro cosas caras y una que ni siquiera es cara — es imposible de saltear:

  1. El universo entero, que no se puede enumerar de otra forma. El Identity Registry de mainnet no expone totalSupply() (verificado en base, ethereum y polygon el 2026-08-10 — ver HANDOFF). No hay for id in 1..N: la única manera de saber qué agentes existen es escanear el evento Registered de cada cadena sobre rangos amplios de bloques, que es exactamente donde un RPC público corta.
  2. La propiedad REAL de cada agente. Registered carga al dueño inicial, que casi siempre es el facilitator que minteó el id antes de transferirlo. Un índice que joinea por ahí archiva a casi todos los agentes bajo el facilitator y no vale nada. Acá el join es por el dueño actual, verificado on-chain.
  3. Cada rating fechado. El resumen que devuelve la cadena trae client, feedbackIndex, value y tags — sin txHash, sin bloque, sin timestamp. La fecha y la trazabilidad salen de leer los logs.
  4. Las revocaciones aplicadas. Revocar muta una fila existente sin mover el contador: cualquier delta basado en contar sirve para siempre un score que incluye un rating revocado.
  5. Una política, versionada, la misma para todos. Vive en describenet/aggregate.py y viaja en cada respuesta como policy_version. Si dos consumidores pudieran obtener números distintos, no habría política.

Un lookup cross-chain que a un tercero le cuesta horas de paginar un RPC de archivo, acá es una llamada.

Y desde el 2026-08-23, ni siquiera hace falta saber dónde mirar

Todo lo de arriba supone que ya sabés a quién estás mirando. Muchas veces no: lo que se tiene en la mano es un string suelto — una dirección EVM, un base58 de Solana, un número. describe_resolve toma eso y contesta qué se interpretó y en qué cadenas existe, con la wallet dueña de cada identidad. Es gratis, y lo es por la misma promesa que el resto de esta página: eso es el número y la ubicación, no la descomposición.

Lo que cierra no es una comodidad, es un agujero. Un agent_id no es un identificador. Los registries EVM mintean un rango denso desde 0 sin un solo hueco, así que un id k existe en toda cadena que tenga más de k agentes — no es una tendencia, es aritmética. Medido el 2026-08-23 sobre la copia local del índice (93.696 agentes, transacción read_only):

agent_id distintos:                                  62.814
  … que existen en 2+ cadenas:                       26.736
  … de ésos, con el MISMO dueño en todas:                 0
  … de ésos, con dueños DISTINTOS:                   26.736

Cero excepciones. Preguntar "¿de quién es el agente 25975?" no tiene una respuesta: tiene tantas como cadenas donde ese id exista, y son personas distintas. Por eso describe_resolve nunca contesta en singular — devuelve una fila por cadena, cada una etiquetada con la lectura que la produjo.

🔴 Esas cifras son de la copia local, no de producción: son dos índices de tamaño distinto. Antes de citarlas como números de producción hay que re-medirlas contra RDS. Lo que no cambia con el tamaño es el cero de la tercera línea, que es el hecho que importa.

Y en Solana el agujero es peor, porque ahí la ambigüedad es sintáctica: el agent_id de un agente y la wallet de una persona son el mismo objeto — 32 bytes en base58, sin prefijo, sin checksum. Se probaron las cuatro vías de separarlos y ninguna sirve. Medido el 2026-08-23 sobre los 1.410 agent ids de Solana de la copia local:

largo de los agent ids:      43 (75)  ·  44 (1.335)
largo de las 253 wallets:    43 (26)  ·  44 (227)      <- el mismo rango
on-curve  (son keypairs):    1.410 de 1.410
off-curve (serían PDAs):     0

Los assets de Metaplex Core son keypairs, no PDAs, así que el truco de mirar si la clave cae en la curva —la única vía que prometía algo— no separa nada. Y no hay prefijo vanity ni checksum donde agarrarse. No existe el regex. La desambiguación tiene que ser por consulta, cuesta dos index scans, y por eso la respuesta trae las dos lecturas etiquetadas, sin elegir: interpretations dice qué permite la forma, y cada fila de matches dice bajo qué lectura la encontró el índice.

¿Y puede un mismo string ser las dos cosas a la vez? Estructuralmente sí: el layout de AssetV1 guarda al dueño como un Pubkey pelado, sin exigir que sea una cuenta de sistema. Medido el 2026-08-23 —el INTERSECT entre los agent_id y los current_owner de solana—: cero casos hoy. Es raro, pero «raro» no es una imposibilidad sobre la que se pueda apoyar código, y por eso la respuesta es una lista y no una etiqueta.

Sin esta puerta, un agente que recibe "25975" y nada más no tenía ningún camino gratis. Corrido el 2026-08-23 contra el servidor:

  • describe_check_wallet("25975")status: error, "wallet inválida", y el hint enumera las dos formas de wallet sin mencionar que existan agent ids;
  • describe_lookup_agent tiene network sin default: es imposible de llamar sin adivinarla;
  • describe_chains sólo confirma cuántas veces hay que adivinar — once cadenas, leído de GET /health el 2026-08-23.

Le quedaba firmar hasta once veces. Y el paywall corre antes del ruteo, así que el 402 sale igual exista o no el agente: hasta diez de esas once firmas se pagan para recibir «no está», con un hint que dice buscá por wallet — el consejo que el llamador no puede seguir, porque si tuviera la wallet no habría preguntado.


Dos transportes, un solo servidor

El código es uno: describenet/mcp_server.py. Se sirve de dos formas, y todo lo que sigue en esta página vale para las dos.

Remoto (Streamable HTTP) — sin instalar nada

Desde el 2026-08-23 el mismo servidor está montado dentro de la API, en https://api.describe.net/mcp. Un cliente que hable Streamable HTTP se conecta ahí y ve exactamente el mismo juego de herramientas — la afirmación de esta sección es la PARIDAD entre los dos transportes, no un número: el conteo vive arriba, medido una sola vez y con su fecha, para no tener dos renglones que haya que acordarse de mover juntos.

{
  "mcpServers": {
    "describe-net": {
      "type": "streamable-http",
      "url": "https://api.describe.net/mcp"
    }
  }
}

Tres cosas del remoto, las tres consecuencia de que corre en AWS Lambda:

  • Sin estado. Cada mensaje JSON-RPC es un POST independiente: el servidor no emite mcp-session-id y acepta tools/call sin initialize previo. Nada vive en memoria entre dos llamadas, porque la siguiente puede caer en otro contenedor.
  • Sólo JSON, sólo POST. Las respuestas son application/json (no SSE) y GET /mcp contesta 405: no hay mensajes iniciados por el servidor y Lambda no puede sostener un stream abierto.
  • La misma caseta de peaje. Adentro de la API las tools llaman a los endpoints en el mismo proceso (transporte ASGI, sin red) y atraviesan el mismo paywall: una tool paga devuelve el 402 de su ruta y se paga con tu firma en payment, exactamente igual que por stdio. Cambia dónde corre el proceso; no cambia nada de cómo se cobra ni quién firma.

Local (stdio)

pip install -r mcp/requirements.txt

mcp/server.py es el lanzador: importa main de describenet/mcp_server.py y lo corre por stdio. Configuración del cliente MCP (Claude Desktop, Claude Code, cualquiera que hable stdio):

{
  "mcpServers": {
    "describe-net": {
      "command": "python",
      "args": ["/ruta/absoluta/al/repo/mcp/server.py"],
      "env": { "DESCRIBENET_API_URL": "https://api.describe.net" }
    }
  }
}

DESCRIBENET_API_URL es la única variable y se lee en cada llamada, no al importar: apuntarla a http://localhost:8088 es todo lo que hace falta para trabajar contra el índice local (make up && make schema && make api).

No hay claves. Este servidor no firma, no custodia nada y no puede mover tus fondos. Tampoco toca la base de datos: le pega a la API HTTP como cualquier otro consumidor.


El loop de decisión

No es "llamar a un endpoint y leer un número". Es esto:

PASO −1 — GRATIS, Y SÓLO SI NO SABÉS QUÉ TENÉS EN LA MANO
describe_resolve(query)            ← ¿qué ES este string y en qué cadenas
                                     existe? Devuelve una fila por cadena con
                                     la wallet dueña ahí. Con eso ya tenés la
                                     llave fuerte —la wallet— para el paso 0.
                                     Si devuelve dos o más filas, NO son el
                                     mismo sujeto: elegí antes de seguir.

PASO 0 — GRATIS, SIEMPRE PRIMERO
describe_check_wallet(wallet)      ← ¿esta wallet TIENE reputación, y en qué
                                     cadenas? Si no tiene, no hay nada que
                                     comprar: acá termina el loop sin gastar.
describe_pricing()                 ← qué cuesta cada cosa, qué tan grande es el
                                     índice hoy, con qué política se calcula.
        │
        ▼
describe_lookup_wallet(wallet)     ← el agregado cross-chain de la contraparte
        │
        ├─ ¿402?  → firmá y repetí la MISMA llamada con `payment=` (abajo)
        │
        ▼
LEER LA COMPOSICIÓN, NO EL NÚMERO
        │
        ├─ distinct_raters  ¿cuántas personas DISTINTAS opinaron?
        ├─ top_client_share ¿hay una que concentra?
        ├─ self_rated.gap   ¿cuánto se sobrevalora?
        └─ caveats[]        las trampas que ESTOS datos disparan, ya escritas
        │
        ▼
describe_facets(wallet)            ← ¿en qué ÁREA es bueno, contra la media del
                                     índice? "gran liveness, pobre activity" es
                                     una decisión; "68" no lo es.
        │
        ▼
describe_history(wallet)           ← ¿venía subiendo o bajando? Un 85 que sube y
                                     un 85 que baja son decisiones opuestas.
        │
        ▼
describe_lookup_agent(net, id)     ← verificación: cada rating con su txHash.
                                     Con esto comprobás que no te mentimos.

La regla que rige todo: nunca decidas con final_score solo. Un score sin su composición es un oráculo; con su composición es evidencia. Por eso cada respuesta sube distinct_raters, top_client_share y self_rated a primer nivel, y por eso el policy_version viaja siempre — incluso cuando falta, donde llega como advertencia explícita en vez de como silencio.

caveats[]: ramificá sobre el code, nunca sobre el text

Desde el 2026-08-28 cada entrada de caveats[] es un objeto, no un string:

{ "code": "burn-address", "text": "Esta wallet es una direccion de quema…" }

El contrato, y es el motivo del campo: text puede cambiar sin aviso —re-redactarse, re-medirse, hasta traducirse—; code jamás. El texto YA cambió una vez y rompió en silencio a todo el que hacía matching de prosa. Los codes no llevan versión ni bumpean policy_version, porque un caveat es advisory por construcción: nombra un CORTE, no mueve un score ni un precio.

Los ocho, y son todos —el set está congelado por test, agregar o renombrar uno se pone rojo a propósito—: no-score · concentration-degraded · single-rater · few-raters · top-client-share · campaign-per-rater · self-rated · burn-address.

Dos cosas que muerden. Una lista vacía no es «verificado limpio»: es «no disparó ninguna trampa sobre los campos de ESTA respuesta». Y en la puerta gratis (describe_check_wallet / GET /wallets/{wallet}/chains) la lista es un subconjunto — sólo lo computable del agregado público, hoy burn-address — así que vacío ahí no promete que la descomposición paga venga callada.

mcp_server.py arma además caveats propios de su presentación (series truncadas, colas recortadas) y salen por la misma puerta con la misma forma, a propósito: dos formas en la misma lista obligarían al consumidor a ramificar sobre la forma antes de poder ramificar sobre el code.


Cómo se paga un 402

Pedir sin pagar no cuesta nada y es el camino previsto: la primera llamada devuelve el desafío, no un error.

  1. Leé el desafío. challenge trae amount, token, el destinatario y supportedChains. Los valores salen de ahí, nunca de una tabla cacheada.
  2. Verificá el destinatario. recipient_check.verdict. La única dirección a la que este servicio pide que le paguen es 0xe4dc963c56979E0260fc146b87eE24F18220e545, y está pinneada en el servidor justamente para poder comparar. Si dice DO_NOT_PAY, no pagues: o el desafío no vino de describe.net, o la tesorería cambió y el servidor no lo sabe. Los dos casos se resuelven preguntando, no firmando.
  3. Firmá. Una autorización EIP-3009 TransferWithAuthorization por ese monto, a ese destinatario, en una de esas cadenas, codificada en base64.
    • desde el MCP de tu wallet: ows_sign_eip3009 (OWS) o pay_x402 (PayBox);
    • o en Python, con el SDK:
      from uvd_x402_sdk import X402Client, X402Config
      
      client = X402Client(config=X402Config(recipient_evm=recipient))
      client.connect_with_signer(...)          # tu clave nunca sale de tu lado
      header = client.create_authorization(     # → el string base64
          pay_to=recipient, amount_usd=Decimal(amount), chain_name="base",
      )
      
  4. Repetí la misma llamada con payment=<base64>. Mismos argumentos, un campo más.

El gas lo paga el facilitator (https://facilitator.ultravioletadao.xyz): vos sólo firmás. Y el nonce de la autorización se consume cuando se liquida, así que una credencial que ya pagó no paga dos veces — una firma por llamada paga.

Si un 4xx llega después de mandar payment, casi siempre es una credencial gastada o firmada por otro monto. Reintentar con la misma falla igual: pedí sin payment, leé el desafío nuevo y firmá contra ése.


Las herramientas

Las ocho gratis — no aceptan payment y no pueden devolver 402:

Herramienta Qué contesta
describe_pricing Qué cuesta cada cosa (sondeado del 402 vivo, nunca de una constante), tamaño vivo del índice, políticas vigentes. Empezá acá.
describe_resolve ¿Qué es este string y dónde existe? Una dirección EVM, un base58 de Solana o un agent id: contesta qué se interpretó (interpretations) y una fila por cadena (matches), cada una con la lectura que la produjo y la wallet dueña ahí. Match exacto: sin prefijo, sin comodín, sin listado y sin búsqueda por nombre. Nunca contesta en singular.
describe_check_wallet ¿Esta wallet tiene reputación, y en qué cadenas? El paso 0 antes de pagar: si acá no hay nada, no hay nada que comprar.
describe_chains Cada cadena indexada lado a lado — o una en detalle (network=), con su top puntuado sólo con los ratings de esa red. stale_hours incluida: la señal de frescura que blocks_behind no puede dar.
describe_feed Las últimas calificaciones del índice entero —o de una sola cadena con network, desde 2026-08-23—, ordenadas por el reloj de la cadena (block_time), con red, valor, faceta y tx_hash.
describe_index_status GET /health servido solo: totales, las cuatro políticas —con los confidence_thresholds que reproducen la banda— y el estado de escaneo por cadena. Para un monitor que no necesita los trece sondeos de precio que dispara describe_pricing (una entrada de _PROBES por cada herramienta que no sea ella misma — contadas el 2026-08-30). (Historia de esta celda, y vale más escrita: decía trece cuando eran doce, se corrigió a doce el 2026-08-23, y volvió a ser trece el 2026-08-29 con describe_rater_profile. El número no es un dato independiente: es tools − 1, y esta celda lo tipeó tres veces a mano en lugar de decir la regla. Ahora dice las dos.)
describe_types Cobertura por tipo autodeclarado, con unknown_share calculado sobre los números vivos.
describe_manifesto El manifiesto como datos: cada principio con el código que lo hace cumplir y cada afirmación con el comando que la verifica.

Las seis pagas — sin payment devuelven el desafío 402, nunca un cobro:

Herramienta Qué contesta
describe_lookup_wallet La reputación de una wallet sumando todas las cadenas, con su composición. snapshot=True deja un recibo replayable.
describe_rater_profile El otro lado del grano: la wallet como CALIFICADORA, no como sujeto. Cuánto emitió, a cuántos sujetos distintos, con qué dispersión (value_stddev cerca de cero = una máquina de estampar, y eso lo leés vos: acá no hay corte ni veredicto), el reparto por cadena, la parte de su sujeto favorito y los roles leídos de tag1. No emite score y no va a emitirlo — rankear calificadores sería un veredicto nuevo encima del que este índice ya se niega a dar. Es el seguimiento natural de un caveat top-client-share o campaign-per-rater: dice si la voz que concentra es una contraparte prolífica o un sello de goma. Envuelve GET /reputation/rater/{wallet} ($0.01), la única ruta paga cuyo 402 no trae free_preview, a propósito: el preview gratis por wallet contesta otra pregunta.
describe_lookup_agent Un agente y cada rating con su transacción — incluidos los revocados, marcados. La herramienta de verificación. Con confidence y caveats[] arriba, los mismos que sirve la puerta HTTP: GET /reputation/agent/{net}/{id} los trae desde el 2026-08-25 y esta tool no los subía, así que quedaban enterrados bajo la lista de ratings — el mismo pago comprando menos contexto por esta puerta. El eco se cura con max_ratings (default 200, 0 = todo) y el recorte siempre se declara.
describe_history Cómo se movió el score, fechado por tiempo on-chain.
describe_facets Reputación por área, comparada contra la media del índice. Sin wallet, la tabla de referencia global (esa variante es gratis).
describe_leaderboard El top ordenado por evidencia, con el número que decidió el orden a la vista y pagination honesta (la API no publica el total; acá viaja null, no inventado). La primera página sale del /leaderboard gratis; paginar va a /leaderboard/page, que cobra.

La descripción completa vive en cada herramienta: qué devuelve, qué cuesta, por qué vale y cómo se lee. Es el producto, no documentación de apoyo — un agente decide si paga leyendo eso.

Salida estructurada (outputSchema): la decisión, no la omisión

El SDK (mcp>=1.23) ya genera outputSchema y structuredContent para todas las herramientas de este servidor: el retorno dict[str, Any] produce {"type": "object", "additionalProperties": true} (medido contra el SDK 1.23.3, no leído del changelog). No se declara un esquema más estricto a propósito. El contrato real de cada herramienta es una unión discriminada por statusok, payment_required, nothing_to_buy, not_found, unreachable, error — y FastMCP valida cada retorno contra el esquema declarado: tipar sólo la rama ok convertiría el desafío 402 (la respuesta más importante del producto) en un error de protocolo justo cuando el agente necesita leerlo. Un esquema que prohíbe la respuesta correcta es peor que un esquema laxo.


Trampas medidas (no hipotéticas)

  • Un agent_id no identifica a nadie por sí solo. El mismo número es un agente distinto —y de otra persona— en cada cadena, y en Solana un agent id y una wallet son sintácticamente indistinguibles. Un integrador que trate un id suelto como llave está agregando reputación de desconocidos. La llave fuerte es la wallet; describe_resolve es la que convierte un id en esa wallet. Las mediciones, arriba.
  • Un 100 de un solo calificador no es un agente mejor: es uno menos observado. Medido el 2026-08-10: de 15.763 wallets con ratings, 7.577 tienen exactamente un calificador, y el 22% de ésas promedia 100 perfecto — contra el 1% entre las de tres calificadores. Por eso el leaderboard no ordena por promedio.
  • top_client_share sola no detecta una campaña. El caso extremo del índice es un agente con 300.001 ratings de 57 calificadores (medido 2026-08-11; una edición anterior de esta página decía 62, que era imposible contra esa medición), todos con el mismo valor y el mismo tag: su share es ~0,14 y un umbral lo dejaría pasar. Lo que lo delata es ratings por calificador.
  • declared_type es autodeclarado. Sale del JSON del agentURI, que es libre. ERC-8004 no tiene campo de tipo. Nunca lo trates como una verificación.
  • tag1 es texto libre on-chain y en este índice hay párrafos de 300 caracteres usados como tag. La vista de facetas está curada por eso, dice cuántas dejó afuera, y el desglose completo sigue en data.
  • concentration: null no es "sin concentración": es la señal caída. La API la degrada antes que fallar la respuesta, y acá eso llega como advertencia.
  • Sin ratings ≠ cero. "No hay evidencia" y "lo calificaron mal" son hechos distintos, y el índice se niega a colapsarlos.
  • undated_reviews > 0 significa que el último punto de la serie puede estar legítimamente por debajo del final_score del perfil. No es un bug de ninguno de los dos.

Enlaces

Todo lo que un integrador (humano o agente) puede querer al lado de este server:

Qué Dónde
OpenAPI (el esquema vivo de la API) https://api.describe.net/openapi.json
Swagger UI https://api.describe.net/docs
ReDoc https://api.describe.net/redoc
Hub de documentación https://docs.describe.net
Skill paraguas para agentes https://describe.net/skill.md
Workflows con curls reales https://describe.net/workflows.md
llms.txt del sitio https://describe.net/llms.txt
Server card MCP https://describe.net/.well-known/mcp/server-card.json
Cómo se paga (x402), en detalle https://describe.net/auth.md
La puerta A2A (message/send) https://describe.net/a2a.md
El badge embebible (gratis) https://describe.net/badge.md

Lo que este servidor NO hace

  • No calcula. Ni una regla de score vive acá; toda herramienta llama al endpoint canónico. Si un consumidor pudiera producir un número distinto llamando por otra puerta, la política habría dejado de ser una.
  • No cobra por su cuenta. Una sola caseta de peaje, en la capa HTTP, con el SDK x402. Este proceso relaya el 402 y reenvía el header firmado.
  • No firma ni guarda claves.
  • No habla con las cadenas ni con Postgres.

Tests

python -m pytest tests/test_mcp.py -q          # las tools, sin red y sin base de datos
python -m pytest tests/test_mcp_remote.py -q   # el remoto (POST /mcp), contra el Postgres local

En test_mcp.py todo entra por un httpx.MockTransport: un test de esta capa que dependiera de la API viva mediría el humor del entorno, y encima cambiaría de resultado según si el paywall está prendido en ese momento. test_mcp_remote.py levanta la app entera con TestClient y prueba el handshake, tools/list contra el server card, una tool gratis con datos reales de la base local —con DESCRIBENET_API_URL apuntando a un host inexistente, para que sólo pase si el transporte in-process está cableado— y que la puerta no cobra pero la tool paga sigue relayando su 402.

La suite está verificada por mutación, no por color: se rompió el desenvoltorio del 402 de FastAPI, el chequeo de destinatario, el envío del header X-PAYMENT, el filtro de tags de prosa, la advertencia de policy_version faltante, la clasificación del 402, el aviso de ratings sin fechar y el precio por defecto — las ocho fallaron un test. Y el 2026-08-21, tres mutaciones más sobre lo nuevo: el has_more de la paginación forzado a False, la trazabilidad del eco contada DESPUÉS del recorte, y la regex de wallet aceptando todo — las tres fallaron el test que las cubre. Un check verde que mira el objeto equivocado es peor que no tener check.

Verbatim source: mcp.md · from mcp/README.md at git:b5eb83903e36 · back to the documentation portal