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
paymenten suinputSchema— 14 tools, 6 conpayment, 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_resolveel 2026-08-23, y trece hasta que entródescribe_rater_profileel 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íadescribenet_mcp). Éste se llamadescribe.nety 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.jsony todo cliente ya instalado lo conocen por eseserverInfo.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:
- 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 hayfor id in 1..N: la única manera de saber qué agentes existen es escanear el eventoRegisteredde cada cadena sobre rangos amplios de bloques, que es exactamente donde un RPC público corta. - La propiedad REAL de cada agente.
Registeredcarga 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. - Cada rating fechado. El resumen que devuelve la cadena trae
client,feedbackIndex,valuey tags — sintxHash, sin bloque, sin timestamp. La fecha y la trazabilidad salen de leer los logs. - 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.
- Una política, versionada, la misma para todos. Vive en
describenet/aggregate.pyy viaja en cada respuesta comopolicy_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_agenttienenetworksin default: es imposible de llamar sin adivinarla;describe_chainssólo confirma cuántas veces hay que adivinar — once cadenas, leído deGET /healthel 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-idy aceptatools/callsininitializeprevio. 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) yGET /mcpcontesta 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.
- Leé el desafío.
challengetraeamount,token, el destinatario ysupportedChains. Los valores salen de ahí, nunca de una tabla cacheada. - Verificá el destinatario.
recipient_check.verdict. La única dirección a la que este servicio pide que le paguen es0xe4dc963c56979E0260fc146b87eE24F18220e545, y está pinneada en el servidor justamente para poder comparar. Si diceDO_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. - Firmá. Una autorización EIP-3009
TransferWithAuthorizationpor ese monto, a ese destinatario, en una de esas cadenas, codificada en base64.- desde el MCP de tu wallet:
ows_sign_eip3009(OWS) opay_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", )
- desde el MCP de tu wallet:
- 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í sinpayment, 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
status — ok, 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_idno 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_resolvees 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_sharesola 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_typees autodeclarado. Sale del JSON delagentURI, que es libre. ERC-8004 no tiene campo de tipo. Nunca lo trates como una verificación.tag1es 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 endata.concentration: nullno 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 > 0significa que el último punto de la serie puede estar legítimamente por debajo delfinal_scoredel 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