En pocas palabras: Un input de 39 bytes se infló a 328 al usar “default” en vez de “prefill” en el schema. Claude recibió cuatro resultados (dos críticos) sobre tres paquetes npm no solicitados, pero los cuatro agentes probados identificaron correctamente que create-react-app tenía riesgo medio, sin repetir el error.
Un desarrollador que mantiene 22 Actors de auditoría en Apify conectó uno de ellos, github-repository-audit, a Claude a través del servidor MCP de Apify. Al pedirle que auditara un solo repositorio, el sistema le devolvió cuatro resultados: tres paquetes npm que nadie mencionó, dos de ellos marcados como riesgo crítico. La causa fue un error en su propio input schema, no un bug de Claude.
La integración Apify MCP Claude funciona así: Apify expone sus Actors (programas alojados en su plataforma) como herramientas que un agente puede invocar mediante el Model Context Protocol de Anthropic. El desarrollador conecta un Actor puntual con un comando de línea de comandos, autentica por OAuth con PKCE y, a partir de ahí, Claude puede llamar esa herramienta como si fuera una función más. El caso que se describe en esta publicación en dev.to muestra qué pasa cuando el schema detrás de esa herramienta tiene una trampa que nadie había detectado.
En este artículo:
- En 30 segundos
- ¿Qué es el Apify MCP server y cómo conecta un Actor con Claude?
- ¿Qué encontró el desarrollador al pedir la auditoría de un solo repositorio?
- ¿Qué diferencia hay entre “default” y “prefill” en un input schema?
- ¿Por qué un agente de IA auditó paquetes que nadie pidió?
- ¿Cómo interpretaron cuatro agentes de IA distintos el mismo resultado erróneo?
- ¿Qué cambios hizo el autor en el Actor tras detectar el problema?
- ¿Qué riesgo revela este caso para quienes conectan Actors o tools a agentes de IA?
- Errores comunes al conectar Actors de Apify a agentes de IA
- Preguntas Frecuentes
- Conclusión
- Fuentes
En 30 segundos
- Un input de 39 bytes (“repos”: [“facebook/create-react-app”]) se convirtió en un inputBodyLen de 328 bytes al pasar por el Actor.
- El Actor agregó automáticamente tres paquetes no solicitados: npm:request, npm:babel-eslint y npm:left-pad, con riesgo high, critical y critical respectivamente.
- La causa fue confundir el campo “default” (se inyecta siempre) con “prefill” (solo es visual en el formulario de la consola de Apify).
- Cuatro agentes de IA distintos, sin conocer el bug, dieron la respuesta correcta sobre create-react-app, pero dos corrieron la auditoría dos veces y tres calificaron la herramienta de defectuosa.
- El desarrollador corrigió el schema moviendo los valores de ejemplo a “prefill” y haciendo que una llamada vacía falle en lugar de usar valores ocultos.
¿Qué es el Apify MCP server y cómo conecta un Actor con Claude?
El Apify MCP server es un servicio de Apify que traduce un Actor (un programa que corre en la infraestructura de Apify) al Model Context Protocol de Anthropic, para que Claude lo pueda invocar como una herramienta más. La conexión se hace con un solo comando de terminal.
En el caso relatado, fue literalmente esta línea:
- claude mcp add –transport http apify: registra el servidor MCP de Apify como fuente de herramientas para Claude.
- ?tools=aiqlabs/github-repository-audit: el parámetro que limita qué Actor ve el modelo. Sin él, Claude tendría acceso a toda la superficie del servidor.
La autenticación corre por OAuth con dynamic client registration y PKCE, así que el desarrollador nunca manejó un token a mano. Ojo con esto: la pantalla de consentimiento de Apify avisa explícitamente que la aplicación “fue registrada dinámicamente y no fue verificada por Apify”, lo cual es honesto pero también una señal de que conviene leer antes de aceptar, no clickear por reflejo. (El detalle gracioso del proceso: el flujo de login pidió una terminal interactiva real, así que hubo que abrir una consola nueva con un archivo batch para completar la autenticación, algo que cualquiera que automatice este paso debería anotar.)
¿Qué encontró el desarrollador al pedir la auditoría de un solo repositorio?
Encontró que un pedido de 39 bytes se transformó en un cuerpo de 328 bytes antes de llegar al Actor, con tres paquetes agregados que nadie había mencionado. El input original era simple: {"repos": ["facebook/create-react-app"]}. Lo que quedó registrado en la plataforma incluía además packages con request, babel-eslint y left-pad, más una serie de parámetros de configuración que el Actor asumió por su cuenta. Para entender cómo funciona Claude en general, esto se conecta con lo que analizamos en nuestra guía completa.
El resultado fue una tabla de cuatro filas para una pregunta sobre un solo repositorio:
| Objetivo | Origen del dato | Nivel de riesgo | ¿Lo pidió el usuario? |
|---|---|---|---|
| facebook/create-react-app | repos | Medium | Sí |
| npm:request | packages | High | No |
| npm:babel-eslint | packages | Critical | No |
| npm:left-pad | packages | Critical | No |

request y left-pad no son paquetes cualquiera. Son dos de los casos más citados de abandono en el registro de npm, así que estaban ahí garantizando findings “interesantes” (entre comillas, porque nadie los pidió). El resumen que genera el Actor, el bloque SUMMARY, mostraba critical: 2 sin ninguna referencia a qué target correspondía cada número. Y el bloque ACTION_LIST, pensado justamente para decirle al usuario qué hacer, ordenaba los cuatro hallazgos por severidad, sin campo de origen: los tres no solicitados quedaban arriba, y el único que sí había pedido el usuario terminaba último.
¿Qué diferencia hay entre “default” y “prefill” en un input schema?
“default” inyecta un valor real cuando el campo se omite, sin importar si la llamada viene de la API, el CLI, un scheduler o un MCP. “prefill” solo rellena el formulario visual de la consola de Apify y nunca llega a la API. Son dos palabras parecidas con comportamientos opuestos, y el desarrollador las usó al revés durante meses.
La especificación oficial de input schemas de Apify lo deja claro, aunque en un párrafo fácil de saltear: default es “el valor que la plataforma pasa automáticamente al Actor si el usuario omite el campo, sea cual sea el medio (API, CLI, scheduler o interfaz de usuario)”. prefill, en cambio, “solo se usa en la interfaz de usuario, pero no afecta la funcionalidad del Actor ni la API”. El desarrollador había puesto los tres paquetes de ejemplo (request, babel-eslint, left-pad) en el campo default de packages, y los repos de ejemplo en el prefill de repos. Cuando el servidor MCP tradujo ese schema a JSON Schema para Claude, default apareció como una palabra clave real que el modelo puede interpretar (“el valor que se usa si se omite esto”), mientras que prefill llegó como una clave sin significado, ignorada por la especificación. El campo que nadie pidió se anunciaba en el idioma del protocolo. El campo que sí importaba no tenía voz. Complementá con a la hora de elegir entre Sonnet y Opus.
¿Por qué un agente de IA auditó paquetes que nadie pidió?
Porque un formulario web muestra los valores precargados y un agente no. En la consola de Apify, el campo Packages aparece con los tres valores ya cargados, visibles, editables: el humano los ve y decide si los borra o los deja. Un agente conectado por MCP nunca ve ese formulario. Manda los campos que decide mandar y recibe filas de resultado, sin la capa visual que delataría el problema.
¿Alguien se dio cuenta antes de que esto llegara a producción? No, y ese es justamente el punto: el bug llevaba meses publicado, sirviendo resultados así a cualquier llamada programática que omitiera el campo packages.
¿Cómo interpretaron cuatro agentes de IA distintos el mismo resultado erróneo?
Los cuatro agentes dieron la respuesta correcta (riesgo medium para create-react-app), pero ninguno llegó ahí gratis. El desarrollador, que ya conocía el bug, decidió no confiar en su propio criterio y le pasó la misma pregunta (“¿es seguro depender de facebook/create-react-app?”) a cuatro agentes frescos que nunca habían visto el Actor.
Los cuatro acertaron, pero con matices que importan tanto como el acierto: dos corrieron la auditoría dos veces para poder separar los datos del repositorio de los datos de los paquetes fantasma, duplicando el consumo de compute units y las consultas contra la API de GitHub (que tiene un límite de 60 solicitudes por hora sin autenticación). Tres de los cuatro calificaron al Actor de “roto” en su respuesta al usuario, una frase que ahora forma parte de cómo se ve la herramienta desde afuera. Y uno dio una respuesta limpia sin mencionar en ningún momento que había descartado tres de las cuatro filas, así que el usuario pagó por cuatro resultados y usó solamente uno, sin enterarse. Lo explicamos a fondo en otra skill instalable en dos comandos.
Uno de los agentes, además, encontró un segundo problema que el propio desarrollador se había perdido: cuando la auditoría corre solo sobre un repositorio (sin datos de registro), tres verificaciones quedan en null, incluida silent_abandonment, que es justamente el hallazgo para el que existe este Actor. El campo devolvía null en lugar de distinguir entre “no se pudo chequear” y “está todo bien”, violando la propia regla que el desarrollador tenía escrita en su README.
¿Qué cambios hizo el autor en el Actor tras detectar el problema?
El cambio principal fue sacar “default” de cualquier campo que defina el objetivo de la auditoría. Los valores de ejemplo se movieron a “prefill”, que sigue siendo útil para que el formulario de la consola no aparezca vacío, pero ya no se filtra hacia la API ni hacia el schema que ve Claude.
- Sin default en campos de target: repos y packages ya no tienen valores inyectados; solo un caller humano o un agente que los pida explícitamente los va a recibir.
- Falla explícita ante llamada vacía: si no se especifica ningún target, el Actor corta la ejecución con un mensaje claro, en vez de rellenar en silencio con paquetes de ejemplo.
- Default reservado para configuración, no para targets: staleAfterDays, que define cuántos días sin actividad cuentan como abandono, sí puede tener un valor por defecto, porque no decide qué se audita.
¿Qué riesgo revela este caso para quienes conectan Actors o tools a agentes de IA?
El riesgo central es que un input schema diseñado para un humano con formulario puede engañar a un agente que solo ve campos y resúmenes. Subís tu Actor, lo probás vos mismo en la consola, ves los valores precargados, los borrás si no te sirven, todo funciona bárbaro; después lo conectás por MCP, un agente lo llama sin pasar por esa pantalla, y el mismo schema que a vos te avisaba en el formulario ahora le entrega al modelo datos que nunca pidió, empaquetados en resúmenes que no distinguen origen.
Como criterio práctico, antes de exponer un Actor propio (o cualquier tool) a un agente por MCP, tiene sentido revisar cada campo con valores por defecto y preguntarse: si el modelo omite este campo, ¿el valor que recibe decide qué se audita, o solo cómo se audita? Si decide qué, ese default probablemente debería ser un prefill, o directamente forzar una falla explícita en vez de rellenar en silencio. Te puede servir nuestra cobertura de sin necesitar una API key propia.
Errores comunes al conectar Actors de Apify a agentes de IA
- Confundir default con prefill: son palabras parecidas en la consola de Apify, pero una llega a la API y la otra no. Revisá la documentación del input schema campo por campo, no asumas por el nombre.
- Exponer todo el servidor MCP sin el parámetro ?tools=: sin esa restricción, el agente ve la superficie completa de Actors disponibles, no solo el que querés que use.
- Confiar en los resúmenes generados por el propio Actor sin revisar las filas crudas: un bloque como SUMMARY puede mezclar hallazgos de distinto origen si no tiene un campo que los separe explícitamente.
- Tratar null como “está todo bien”: cuando una verificación no se pudo correr por falta de datos, devolver null en lugar de un estado explícito hace que tanto humanos como agentes lean silencio como resultado positivo.
Preguntas Frecuentes
¿Qué es el Apify MCP server y cómo se conecta a Claude?
Es el servicio de Apify que expone Actors como herramientas invocables por el Model Context Protocol de Anthropic. Se conecta con un comando de CLI (claude mcp add --transport http apify), autenticando por OAuth con PKCE, y se puede limitar a un solo Actor usando el parámetro ?tools= en la URL. Cobertura relacionada: un caso donde Claude quedó afuera del GA.
¿Qué diferencia hay entre “default” y “prefill” en un input schema?
Default inyecta un valor real cuando el campo se omite, llegando a la API, al CLI y a cualquier agente conectado por MCP. Prefill solo rellena el formulario visual de la consola de Apify y nunca se envía a la API.
¿Por qué un agente de IA auditó paquetes que nadie pidió?
Porque el input schema del Actor tenía tres paquetes de ejemplo cargados en el campo “default” en lugar de “prefill”. Al omitirse el campo packages en la llamada, la plataforma los inyectó automáticamente, sin que el usuario ni el agente lo hubieran solicitado.
¿Cómo se detecta que un paquete npm está abandonado si su repositorio fue archivado?
Comparando lo que dice el registro de npm contra lo que dice el repositorio de origen. En el caso documentado, npm seguía sirviendo [email protected] sin ninguna advertencia de deprecación, mientras el repositorio kentcdodds/cross-env aparecía archivado en GitHub, una discrepancia que npm install no muestra en ningún momento.
¿Es seguro dar acceso OAuth a un Actor de Apify desde Claude?
El flujo usa OAuth con PKCE y registro dinámico de clientes, y Apify muestra un aviso explícito de que la aplicación no fue verificada. Conviene leer esa pantalla de consentimiento antes de aceptar y limitar el acceso con ?tools= a un único Actor en vez de exponer todo el servidor MCP.
Conclusión
Lo que cambió acá no es el modelo ni el protocolo, es la manera de pensar un input schema. Un campo pensado para un formulario humano, con valores de ejemplo visibles y editables, se convierte en una fuente de datos falsos apenas un agente lo consume sin esa capa visual. Los cuatro agentes probados no se equivocaron en la respuesta final, pero pagaron de otras formas: compute duplicado, una reputación golpeada por un comentario de “herramienta rota”, o directamente cero transparencia sobre qué se descartó.
Si mantenés Actors, tools o cualquier función expuesta a un agente por MCP, el ejercicio vale la pena: agarrá tu propio schema, buscá cada “default”, y preguntate si ese valor decide qué hace la herramienta o solo cómo lo hace. La respuesta a esa pregunta es la diferencia entre un campo inofensivo y una trampa que nadie va a ver hasta que un agente la dispare por vos.
Fuentes
- Dev.to – “I connected my audit Actor to Claude, and it audited three packages nobody asked for”, relato original del desarrollador
- Apify MCP server – endpoint del servidor MCP mencionado en el caso
- Model Context Protocol – especificación oficial del protocolo de Anthropic
- Apify – plataforma de Actors mencionada en el artículo
