Python Code Validator
Comprueba que el Python generado por IA hace lo que pediste: expresa la intención como afirmaciones y el servidor ejecuta el código en un contenedor aislado, devolviendo una corrección solo cuando todos los ejemplos pasan.
Documentación
Validador de Código Python
Un servidor MCP que valida, repara y ejecuta Python contra los ejemplos que
se supone debe satisfacer — validate_python, repair_python y execute_python
por HTTP en https://api.statemind.ai/mcp, con una clave gratuita y sin cuenta.
Un servicio alojado que demuestra que el Python generado por IA hace lo que
pediste. Indica la intención — aserciones o líneas de doctest — y el código se
ejecuta contra ella dentro de un contenedor sin red y con un sistema de archivos
de solo lectura; una corrección solo regresa cuando cada ejemplo pasa. En los
defectos de QuixBugs, eso es un 41% reparado y un 77% rechazado por no hacer lo
que dicen, sin falsas alarmas en los programas corregidos — donde
ruff y mypy marcan el defecto en ninguno de ellos
(los números).
Las comprobaciones que no necesitan intención vienen incluidas: diagnósticos de
sintaxis y lint, una política de seguridad AST que también detecta llamadas
ocultas detrás de importaciones dinámicas y búsquedas de atributos en tiempo de
ejecución, una pasada de bandit, un escaneo de credenciales y reparación
determinista — un veredicto con una puntuación. Hacer la misma pregunta dos veces
en diez minutos se responde desde la primera respuesta y no cuesta nada
(x-msvc-repeat: 1).
Este repositorio contiene el lado del cliente: la configuración de MCP, el
script de CI y el hook de pre-commit. El servicio en sí se ejecuta en
https://api.statemind.ai, por lo que no hay nada que instalar ni alojar.
Una clave, sin cuenta
curl -s -X POST https://api.statemind.ai/v1/keys
# {"api_key": "msvc_free_…", "tier": "free", "calls_per_day": 25, "modes": ["static"]}
25 comprobaciones estáticas al día, medidas por día UTC, y unas pocas claves por
dirección: suficiente para probarlo y ejecutarlo en un proyecto pequeño, no un
suministro. Cada respuesta lleva el estado de la asignación (x-quota-remaining,
x-quota-reset), para que un cliente pueda retroceder antes de que se corte.
MCP
Registrado en el registro oficial de MCP como
ai.statemind/python-code-validator, un nombre verificado contra el dominio que
lo sirve en lugar de una cuenta de GitHub. Cualquier cliente MCP lo añade con un
bloque:
{
"mcpServers": {
"python-code-validator": {
"type": "http",
"url": "https://api.statemind.ai/mcp",
"headers": { "Authorization": "Bearer msvc_free_…" }
}
}
}
- Claude Code:
claude mcp add --transport http python-code-validator https://api.statemind.ai/mcp --header "Authorization: Bearer msvc_free_…" - Cursor:
~/.cursor/mcp.json, mismo bloque. - VS Code / Copilot:
.vscode/mcp.jsonbajo"servers".
Un cliente que solo lanza un comando usa el puente stdio de este repositorio en su lugar, que reenvía la misma herramienta por HTTPS:
{
"mcpServers": {
"python-code-validator": {
"command": "python3",
"args": ["/path/to/python-code-validator/mcp_stdio.py"]
}
}
}
O como contenedor, que el Dockerfile de aquí construye:
docker build -t python-code-validator .
docker run -i --rm -e VALIDATOR_API_KEY python-code-validator
Gemini CLI instala el mismo puente como extensión, con el archivo de instrucción que hace que se use:
gemini extensions install jkanselaar/python-code-validator
Tres herramientas, nombradas por lo que hacen al código:
| herramienta | ejecuta el código | clave |
|---|---|---|
validate_python | no | gratuita |
repair_python — también devuelve fixed_code | no | de pago |
execute_python — también lo ejecuta en un sandbox | sí | de pago |
La antigua herramienta única python_code_validator, con su argumento mode,
sigue respondiendo para clientes que ya la configuraron, pero ya no aparece en la
lista.
Decir lo que se suponía que el código debía hacer
Cada comprobación anterior pasa en una función que calcula la respuesta incorrecta. Lo único que lo detecta es la intención, y el agente que pidió el código es el único que la tiene — así que pásala:
{"code": "def bitcount(n): …", "mode": "execute",
"options": {"examples": "assert bitcount(127) == 7"}}
Las líneas de doctest (>>> bitcount(127) y luego 7) funcionan de la
misma manera, al igual que los ejemplos >>> ya escritos en el
código fuente. execute_python los ejecuta en el sandbox: uno que no se cumple
es un error python:example-mismatch, y la búsqueda de reparación devuelve una corrección
solo cuando cada ejemplo pasa. En el conjunto de defectos de QuixBugs — errores
reales, entradas de prueba ocultas que deciden la corrección — eso repara el 41%
y rechaza el 77% por no hacer lo que dicen, sin falsas alarmas en los programas
corregidos.
Repetir una llamada no cuesta nada: la misma clave preguntando la misma
pregunta — mismo modo, mismo código, mismos ejemplos — se responde desde la
respuesta que ya obtuvo, marcada x-msvc-repeat: 1, para que un agente que
verifica su trabajo en cada paso no pague por veredictos que no pueden haber
cambiado.
Plugin de Claude Code
Una instrucción se puede ignorar; un hook no. El plugin comprueba cada archivo Python que Claude Code escribe o edita, en el turno en que se escribió, y devuelve los errores al modelo en lugar de a ti:
/plugin marketplace add jkanselaar/python-code-validator
/plugin install python-code-validator@statemind
Nada que configurar: crea y guarda su propia clave gratuita en el primer uso. Un archivo que vuelve aceptado es silencioso, uno rechazado detiene el turno con las líneas infractoras nombradas, y un archivo idéntico no se pregunta dos veces. Nunca termina una sesión por sus propios problemas — un servicio inalcanzable o una asignación agotada deja que el turno continúe, y la asignación dice cómo aumentarla.
Establece VALIDATOR_API_KEY para usar una clave de pago en lugar del nivel
gratuito, y VALIDATOR_URL para apuntar a tu propio despliegue. El plugin
también lleva la habilidad validate-python, para la parte que un hook no puede
hacer: declarar la intención como ejemplos y ejecutar el código contra ellos.
Hook de Cursor
El mismo script, conectado al postToolUse de Cursor, donde el veredicto
vuelve como contexto en la conversación en lugar de como código de salida:
mkdir -p .cursor/hooks
base=https://raw.githubusercontent.com/jkanselaar/python-code-validator/main
curl -sf $base/plugin/hooks/validate_written.py -o .cursor/hooks/validate_written.py
curl -sf $base/cursor/hooks.json -o .cursor/hooks.json
Los hooks de proyecto se ejecutan desde la raíz del proyecto, por eso el comando
en cursor/hooks.json es una ruta relativa a ella. Para un hook
que se aplica a cada proyecto en su lugar, pon el script en ~/.cursor/hooks/ y
el mismo bloque en ~/.cursor/hooks.json con el comando
python3 ./hooks/validate_written.py --cursor.
Hacer que el agente lo use
Configurar el servidor no es lo que hace que se llame: el archivo de instrucción
lo es. AGENTS.md en este repositorio es ese texto,
escrito para dejarse caer en cualquier proyecto bajo el nombre que el cliente
lea:
mkdir -p .github
curl -sf https://raw.githubusercontent.com/jkanselaar/python-code-validator/main/AGENTS.md \
| tee AGENTS.md CLAUDE.md GEMINI.md .github/copilot-instructions.md >/dev/null
Cursor lee reglas con front matter en su lugar, así que esa es un archivo
separado — copia .cursor/rules/python-code-validator.mdc
en .cursor/rules/ del proyecto.
La versión corta, si prefieres añadir una línea a las instrucciones que ya tienes:
Escribe lo que el código debería hacer como ejemplos
assertantes de escribir el código, y pásalos enoptions.examples. Llama avalidate_pythondespués de cada edición y aexecute_pythonuna vez que una función esté terminada, no de nuevo hasta que lo que hace haya cambiado. Cuando una llamada devuelvafixed_code, tómala — el servicio lo ejecutó contra tus ejemplos. No presentes código que volvióvalid: false.
CI
El servicio entrega el cliente, así que un flujo de trabajo no necesita checkout de este repositorio ni secreto:
- run: |
curl -sf https://api.statemind.ai/v1/client -o validate.py
python3 validate.py --changed-against "origin/${{ github.base_ref }}"
O como acción, desde el Marketplace:
permissions:
contents: read
pull-requests: write # so the run can comment its result on the pull request
steps:
- uses: jkanselaar/python-code-validator@v1.22.0
with:
api-key: ${{ secrets.VALIDATOR_API_KEY }} # optional; free tier without it
El Python cambiado se valida y las líneas infractoras se anotan en el diff, fallando el trabajo en errores de sintaxis y patrones inseguros. Los archivos que el servicio rechaza directamente (por su límite de 200 kB) se omiten con una advertencia en lugar de fallar la ejecución.
La ejecución también deja un comentario en la solicitud de extracción, editado en
su lugar en empujes posteriores en lugar de repetirse: qué se aceptó, qué se
reparó y cuánto de la asignación del día queda. Sin pull-requests: write no se
escribe nada y el trabajo no se ve afectado; comment: "false" lo desactiva.
En el nivel gratuito, la acción guarda su clave en la caché del flujo de trabajo,
una por repositorio por día, así que la asignación pertenece al repositorio en
lugar de a la ejecución. Con api-key establecido, la caché se omite.
Pre-commit
repos:
- repo: https://github.com/jkanselaar/python-code-validator
rev: v1.22.0
hooks:
- id: python-code-validator
El cliente en sí
validate.py es solo biblioteca estándar, así que también funciona como
python validate.py file.py en un Makefile, un hook de git o un contenedor:
$ python3 validate.py service.py
::error file=service.py,line=88,title=SyntaxError::invalid syntax
FAIL service.py score=0.66
0/1 files accepted
VALIDATOR_API_KEY se usa cuando está establecido; de lo contrario, el cliente crea
una clave gratuita — guardándola en VALIDATOR_KEY_FILE cuando eso nombra una ruta,
que es como una serie de ejecuciones comparte una asignación. VALIDATOR_URL lo
apunta a otro despliegue. VALIDATOR_SOURCE nombra al llamante, que solo se cuenta:
una ejecución dentro de un flujo de trabajo dice github-action por sí misma.
La insignia
Un repositorio cuyo Python se comprueba en cada solicitud de extracción puede decirlo:
[](https://api.statemind.ai/?src=badge)
HTTP
curl -s https://api.statemind.ai/v1/validate \
-H "Authorization: Bearer $VALIDATOR_API_KEY" \
-H 'content-type: application/json' \
-d '{"code": "def f(:\n pass\n", "mode": "static"}'
mode es static, repair o execute;
repair y execute necesitan una clave configurada. El código
enviado no se registra.
Una llamada rechazada dice qué hacer al respecto, para que un llamante sin operador al que preguntar pueda resolverlo por sí mismo:
{"error": "payment_required",
"remedy": {"action": "upgrade_key", "hint": "A free key covers static only. …"}}
Pagar por llamadas
Una clave gratuita cubre 25 comprobaciones estáticas al día, y una dirección obtiene unas pocas claves al día, así que la asignación es una prueba en lugar de un suministro. Más allá, una clave lleva créditos: una comprobación estática cuesta 1, una reparación 3 y una ejecución en sandbox 10, y una llamada idéntica repetida dentro de diez minutos se responde desde la primera gratis.
Los créditos se compran con una tarjeta, sin factura ni nadie a quien preguntar:
curl -s -X POST https://api.statemind.ai/v1/keys/checkout \
-H 'content-type: application/json' \
-d '{"api_key": "'"$VALIDATOR_API_KEY"'", "credits": 500}'
Eso responde con una página de Stripe Checkout; los créditos están en la clave
segundos después de que la tarjeta se liquide (500 créditos son €10). Un agente
con una billetera Gnosis puede en su lugar pagar en xDAI sin navegador —
GET /v1/pricing indica ambas rutas.
Ejemplos
examples/ contiene tres archivos y el cliente para
enviarlos: uno que pasa cada comprobación y aún así devuelve el número
incorrecto, uno que la política de seguridad rechaza, y uno que vuelve aceptado
desde el sandbox.
Licencia
MIT.