@izytesting/izytesting-mcp
v0.1.1-beta.4
Published
Servidor MCP de IzyTesting: expone requerimientos y casos de prueba a cualquier cliente MCP (Claude Code, IDEs, aplicaciones propias).
Readme
IzyTesting MCP
Servidor MCP que expone IzyTesting a cualquier cliente compatible: Claude Code, IDEs con soporte MCP, o aplicaciones propias.
Un desarrollador le pregunta a su editor "¿qué casos de prueba tiene la historia del login?" y obtiene la respuesta sin abrir IzyTesting.
El principio que ordena todo
El servidor actúa como el usuario que lo corre, nunca como el sistema.
Cada persona trae su propio token y solo ve lo que ya podía ver entrando a IzyTesting. Los permisos, roles y límites de su cuenta siguen aplicando: este servidor no los puede saltear porque no tiene con qué.
Por eso nunca hay que configurar acá la clave de servicio del agente
(IZY_AGENT_KEY). Esa clave puede acuñar el token de cualquier usuario; si
viajara a la máquina de cada desarrollador, cualquiera podría actuar como
cualquiera. El servidor detecta ese error de configuración y lo rechaza al
arrancar.
Instalación
npx @izytesting/izytesting-mcpNo tiene dependencias. Requiere Node 20 o superior.
Configuración
| Variable | Obligatoria | Qué es |
|---|---|---|
| IZYTESTING_URL | sí | Dirección del backend de IzyTesting |
| IZYTESTING_TOKEN | sí | Clave MCP (izy_mcp_...) o JWT de sesión para pruebas |
| IZYTESTING_ORG | no | Organización. Si no está, se usa la de tu cuenta |
| IZYTESTING_READONLY | no | true oculta las herramientas que modifican datos |
| IZYTESTING_TIMEOUT_MS | no | Espera máxima por llamada (30 s por defecto) |
La organización no va en el JWT: se resuelve con el perfil de la cuenta, igual
que al entrar a IzyTesting. Solo hace falta IZYTESTING_ORG si tenés varias y
no querés la que está por defecto.
En Claude Code
{
"mcpServers": {
"izytesting": {
"command": "npx",
"args": ["-y", "@izytesting/izytesting-mcp"],
"env": {
"IZYTESTING_URL": "https://izytesting.tu-dominio.com",
"IZYTESTING_TOKEN": "..."
}
}
}
}Para muchos usuarios a la vez: modo HTTP
Lo de arriba sirve cuando el cliente puede lanzar el proceso y hay una persona por proceso (Claude Code, la terminal). Un cliente que corre en la nube —Copilot Studio— no puede ejecutar nada en tu máquina: necesita una URL. Y un cliente compartido por un equipo entero —LibreChat— necesita algo más: que un solo servidor pueda atender a muchas personas sin confundirlas.
IZYTESTING_HTTP_KEY=... npx izytesting-mcp-http| | |
|---|---|
| POST /mcp | Streamable HTTP, la sesión en la cabecera Mcp-Session-Id |
| GET /mcp | El canal servidor→cliente |
| DELETE /mcp | Cierra la sesión |
| GET /sse + POST /messages | SSE heredado, para clientes que todavía lo piden |
| GET /salud | Sin llave, para mirarlo desde afuera |
Habla los dos transportes porque las plataformas no coinciden en cuál usan, y responde en JSON o en SSE según lo que el cliente acepte.
No es otro servidor. http.js importa el mismo crearServidor que usa
stdio, así que las herramientas, sus permisos y su comportamiento son los
mismos. Si algo cambia en tools.js, cambia en los dos transportes a la vez, y
la prueba que cuenta las herramientas corre contra el HTTP también.
Dos secretos que responden preguntas distintas
Antes eran uno solo, y de ahí venía la limitación de este modo: la cabecera
Authorization entrante se consumía como llave de portería y se descartaba, así
que el servidor sólo podía tener una identidad —la de IZYTESTING_TOKEN— y
IzyTesting veía a todos los usuarios como la misma persona.
Ahora están separados:
| Cabecera | Pregunta que responde | Quién la pone |
|---|---|---|
| X-Izy-Mcp-Key | ¿Este cliente es quien debe hablarme? | El administrador, una vez |
| Authorization: Bearer … | ¿Quién es el usuario? | El cliente, en cada llamada |
IZYTESTING_HTTP_KEY es obligatoria y el servidor se niega a arrancar sin
ella. Fallar al arrancar es molesto; quedar abierto sin que nadie se entere es
peor. Se compara en tiempo constante. Por defecto escucha sólo en 127.0.0.1:
exponerlo a la red es una decisión aparte, con IZYTESTING_HTTP_HOST.
En modo shared se siguen aceptando las formas viejas de mandar la llave
(Authorization: Bearer, X-API-Key, Api-Key), para no romper lo que ya
está configurado. En passthrough sólo vale X-Izy-Mcp-Key: confundirlas
dejaría entrar a cualquiera que traiga un token de usuario válido.
| Variable | Para qué |
|---|---|
| IZYTESTING_HTTP_KEY | La llave de portería. Obligatoria |
| IZYTESTING_HTTP_PORT | Puerto, por defecto 8787 |
| IZYTESTING_HTTP_HOST | Interfaz, por defecto 127.0.0.1 |
| IZYTESTING_HTTP_SESSION_TTL_MS | Vida de una sesión sin uso, por defecto 30 min |
Un servidor, muchos usuarios
En passthrough —el modo por defecto de HTTP— la identidad la trae cada
petición y el servidor no tiene una propia. Es lo que permite que LibreChat
lo configure una vez, de forma central, y que ningún usuario tenga que pegar una
API key en ningún formulario: LibreChat reenvía el access token que IzyTesting
—que también es proveedor OIDC— emitió para cada persona.
Cómo se sostiene el aislamiento:
- La identidad viaja por contexto, no por argumento.
AsyncLocalStoragela lleva por petición, así que las 55 herramientas la ven sin recibirla y el modelo no puede verla, inventarla ni suplantarla. Una herramienta a la que le llegue elemailde otra persona sigue actuando como quien llamó. - Cada sesión pertenece a quien la abrió. Un pedido con el id de sesión de otro responde 403, en vez de heredar su contexto.
- Lo que se deriva de la identidad va indexado por ella. La organización y el proyecto activo se recuerdan por usuario, no en la instancia compartida.
IZYTESTING_AUTH_MODE=shared vuelve al comportamiento anterior si hace falta.
Qué puede hacer
Cincuenta y cinco herramientas —veintinueve de consulta y veintiséis que modifican datos—, nombradas por lo que una persona quiere hacer y no por el endpoint que lo resuelve.
Empezaron siendo ocho, con un límite en mente: pasadas unas diez, el modelo elige peor. Eso sigue siendo cierto, y por eso las que se sumaron después no compiten entre sí. Están agrupadas por bloque, y dentro de cada bloque el camino es casi siempre único: quien va a generar casos no tiene cuarenta y dos opciones, tiene tres en orden. La descripción de cada una dice cuándo usarla y a cuál pasar después, así que el modelo casi nunca elige entre pares parecidos.
Con IZYTESTING_READONLY=true quedan solo las veintitrés de consulta. Si alguien
llama una de las otras, la respuesta explica por qué no está disponible en vez
de fallar sin más.
Las 42, por bloque
Consultar y editar lo que ya existe — 10
| Herramienta | Para qué | Modifica |
|---|---|---|
| buscar_requerimientos | Por texto, prioridad, estado, tipo o responsable | |
| ver_requerimiento | Detalle completo por clave (REQ-125) | |
| buscar_casos | Por texto, estado, prioridad, suite, tag, milestone o asignado | |
| ver_caso | Detalle completo con sus pasos (TC-31) | |
| listar_miembros | Convierte un nombre en el id que piden asignar y los filtros | |
| asignar | Asigna un responsable a requerimientos, casos, suites o automatización | sí |
| listar_proyectos | Los proyectos de la organización, con el activo marcado | |
| editar_requerimiento | Actualiza campos de un requerimiento | sí |
| editar_caso | Actualiza campos de un caso | sí |
| editar_pasos | Agrega, modifica, elimina o reordena los pasos de un caso, funcional o automatizado | sí |
Redactar requerimientos y generar casos — 7
| Herramienta | Para qué | Modifica |
|---|---|---|
| crear_requerimientos | Redacta historias con el motor de IzyTesting desde cualquier insumo; con confirmación las guarda | sí |
| generar_casos | Paso 1: genera el inventario de casos propuestos | sí |
| redactar_casos | Paso 2: redacta en detalle los que el usuario aprobó | sí |
| guardar_casos | Paso 3: persiste los casos en IzyTesting | sí |
| procesos_en_curso | Trabajos de IzyTesting corriendo ahora | |
| estado_generacion | Consulta un trabajo y devuelve el resultado | |
| fijar_proyecto | Cambia el proyecto activo de la cuenta | sí |
Datos de prueba — 2
| Herramienta | Para qué | Modifica |
|---|---|---|
| generar_datos_prueba | Datos concretos para cada paso de un caso | |
| generar_datos_varios_casos | Lo mismo para varios casos o un requerimiento | |
Bugs — 5
| Herramienta | Para qué | Modifica |
|---|---|---|
| buscar_bugs | Bugs de la organización, o los de un caso | |
| casos_para_bug | Casos candidatos para asociar a un bug | |
| redactar_bug | Le pide a IzyTesting que redacte el defecto desde el paso que falló y lo observado | |
| crear_bug | Crea un bug, opcionalmente ligado a un caso | sí |
| asociar_bug | Vincula o desvincula un bug con casos | sí |
Integraciones — 3
| Herramienta | Para qué | Modifica |
|---|---|---|
| listar_integraciones | Credenciales de Jira/Azure/GitLab y sus proyectos | |
| importar_desde | Trae issues del origen; con confirmación los guarda | sí |
| exportar_a | Manda casos, bugs o requerimientos al destino | sí |
Automatización — 13
| Herramienta | Para qué | Modifica |
|---|---|---|
| listar_acciones | El catálogo de 75 acciones del motor (64 WEB, 11 API) | |
| listar_agentes | Agentes de ejecución y su estado | |
| listar_automatizacion | Páginas, objetos y casos automatizados del proyecto | |
| preparar_captura | Las instrucciones de qué mirar en la página, antes de abrirla | |
| crear_pagina_con_objetos | Crea una página y sus elementos, desde lo que se vio en el navegador | sí |
| crear_caso_automatizado | Crea el caso y sus pasos (acción + objeto + valor) | sí |
| estabilizar_caso | Ejecuta uno o varios con un agente real para comprobar los localizadores | sí |
| resultado_estabilizacion | El resultado paso por paso de la última ejecución | |
| preparar_automatizacion | Reúne lo necesario para convertir un caso funcional | |
| analizar_priorizacion | Las señales para decidir qué automatizar primero | |
| preparar_escenarios | Señales y propuesta de qué casos van juntos en un escenario, con el porqué | |
| crear_escenario | Crea el escenario y le agrega los casos en orden de ejecución | sí |
| crear_plan_y_ejecutar | Arma el plan con su configuración y, si se lo piden, lanza la corrida real | sí |
Agrupar y ejecutar a mano — 3
| Herramienta | Para qué | Modifica |
|---|---|---|
| agrupar_en_hito | Cuelga de un hito los conjuntos que elijas, y crea uno nuevo si hace falta | sí |
| preparar_ejecucion | Los casos de un conjunto, o un caso con sus pasos listos para ejecutar | |
| registrar_ejecucion | Guarda la corrida: estado por paso y capturas como evidencia | sí |
Convertir y priorizar: el servidor reúne, el modelo decide
Ni traducir prosa a acciones ni priorizar se resuelven con una fórmula. Las dos dependen del contexto, y una fórmula escondida en el servidor sería peor que una recomendación explicada.
preparar_automatizacion entrega los pasos del caso funcional tal como están
escritos, las 64 acciones WEB disponibles, y las páginas y objetos que ya
existen agrupados. El mapeo lo hace el modelo. Si no hay objetos donde apoyarse,
lo dice y manda a preparar_captura.
analizar_priorizacion no devuelve un puntaje. Devuelve las señales
—prioridad, estado, si ya está automatizado, bugs asociados, cuántos casos
comparten requerimiento— y una nota de cómo leer cada una. Así la recomendación
sale con su porqué: "estos ocho primero, porque comparten el login y tres
tienen bugs recurrentes", en vez de un número sin contexto.
Una salvedad honesta: el cruce entre el caso funcional y su versión automatizada se hace por nombre, porque IzyTesting no guarda un vínculo explícito entre los dos. Si alguien renombró uno de los dos, puede aparecer como no automatizado cuando sí lo está. Queda avisado en la propia respuesta.
Estabilizar no es opcional
Hasta que el caso corre con un agente real, los selectores son una suposición. La validación de la captura descarta lo evidentemente malo, pero no puede saber si un selector apunta al elemento correcto: eso solo lo dice ejecutarlo.
Se comprobó en el propio desarrollo de este servidor. Se creó el caso CP-11 con
selectores plausibles para la demo de OrangeHRM, la validación los aceptó —eran
estables y bien formados— y al estabilizarlo falló el primer paso. Los
selectores estaban mal, y solo la ejecución lo reveló.
Por eso estabilizar_caso nunca afirma éxito sin evidencia: si la ejecución
termina pero no llegan los pasos, dice que no se puede afirmar nada en vez de dar
un visto bueno. Un "los localizadores funcionan" sin datos que lo respalden es
peor que no decir nada.
Editar los pasos: un caso, dos modelos
Un caso guardado se corrige en el paso, no rehaciéndolo entero. editar_pasos
agrega, modifica, elimina y reordena, y sirve para los dos tipos de caso —que en
IzyTesting son dos modelos distintos con el mismo nombre:
| | Funcional (TC-##) | Automatizado (CP-##) |
|---|---|---|
| Qué es un paso | prosa: qué se hace y qué se espera | acción del catálogo + objeto |
| Dónde vive | proyecto/ (Step.step_num) | automation/ (Step.order) |
| Cómo se edita | cuatro endpoints sueltos, reemplazo total | ViewSet REST, parcial |
| Renumera solo | no | sí, al borrar |
El paso se identifica por su posición, nunca por su id. Es lo que se ve en
ver_caso y en la pantalla; los ids no le dicen nada a nadie. La traducción a
ids pasa adentro del servidor, y el tipo de caso se detecta solo desde la clave.
Lo que el backend no hace y esta herramienta sí:
create_stepno corre los pasos siguientes: insertar en el medio dejaba dos con el mismostep_num. Se crea al final y se reordena después.delete_stepno renumera lo que queda: borrar el 2 dejaba 1, 3, 4.update_Stepno es parcial: pisaaction,expected_resultydataStepcon lo que reciba, y responde 500 si falta cualquiera de las tres claves. Se lee el paso y se reenvía lo que no cambia, igual que eneditar_caso.- En automatización
(test_case, order)es único: no se puede insertar en el medio de una sola vez, y borrar de adelante para atrás borraba el paso equivocado, porque ahí el backend sí renumera después de cada borrado.
Un campo del otro modelo —objeto en un caso funcional, resultado_esperado en
uno automatizado— se rechaza con el motivo. El backend los ignoraría en
silencio y el paso quedaría sin cambiar sin que nadie se entere.
editar_pasos reemplazó a agregar_paso, que hacía una de las cuatro
operaciones, en uno de los dos modelos, pidiendo el id numérico del caso.
Automatización: quién pone el navegador
El modelo de IzyTesting es
Página → Objetos (selectores) → Pasos (acción + objeto + valor) → Caso.
Un paso funcional es prosa ("hacer clic en el botón Cambiar"); uno automatizado es una acción del catálogo más un objeto con su selector. En la aplicación web los selectores salen de grabar con el Live Browser. Acá el reparto es otro:
| Quién | Hace | |---|---| | El cliente MCP (Claude) | Abre la URL, mira la página, propone los selectores | | Este servidor | Crea la página, los objetos y los pasos |
El servidor no necesita navegador, igual que no necesita leer URLs para generar casos. Pero el cliente sí necesita uno de verdad: una herramienta que convierta la página a texto (tipo fetch) no sirve, porque las aplicaciones modernas se arman con JavaScript y no hay DOM que inspeccionar. Hace falta un navegador real, como la extensión de Chrome de Claude.
Que el cliente lo haga bien, no solo que lo haga
Un agente puede inventar selectores plausibles sin haber abierto nada. Quedan guardados como si fueran buenos y fallan recién al ejecutar, cuando ya nadie se acuerda de dónde salieron. Como el servidor no puede mirar la página, no puede comprobar que el selector sea correcto — pero sí puede negarse a guardar lo que evidentemente no salió de una inspección real:
urles obligatoria. Sin ella no hay forma de volver a mirar.etiquetaytagson obligatorias por elemento. Son la prueba de que alguien miró: no se deducen del nombre que uno le quiera poner.- Se rechaza el XPath absoluto, las clases generadas por el framework
(
css-1a2b3c,ng-tns-c12), tres o más índices posicionales, y que dos elementos compartan selector. - Se avisa cuando el selector es más frágil de lo necesario.
Si algo no pasa, no se guarda nada —ni la página— y el error dice qué
corregir. preparar_captura entrega ese mismo contrato por adelantado, para que
cualquier agente sepa qué traer antes de abrir la URL.
Verificado contra producción: se creó la página 395 con cuatro objetos y el caso
CP-11 con tres pasos.
De estabilizar a la corrida real: escenario, plan, ejecución
Un caso estabilizado todavía no es nada por sí solo. Para que corra de verdad hay que subirlo por dos niveles, y el modelo de IzyTesting define qué significa cada uno:
| | Agrupa | El criterio es |
|---|---|---|
| Escenario (SC-XXX) | casos, en orden | qué corre junto y en qué secuencia |
| Test plan (TP-XXX) | escenarios | en qué entorno y con qué frecuencia corre |
Un escenario es una secuencia, no una carpeta. EscenarioTestCase.order es
único por escenario, y el agente recibe escenarios enteros —no casos sueltos—
como unidad de trabajo. Por eso el orden del arreglo que se le pasa a
crear_escenario es orden de ejecución real.
El plan agrupa por cadencia, no por tema. Ahí viven el agente, los navegadores, la resolución y la frecuencia. Un smoke diario y una regresión semanal son dos planes aunque compartan escenarios.
Confundir los dos niveles —meter en un escenario todo lo de un tema, o en un plan todo lo de un sprint— es lo que después hace que las corridas fallen por dependencias que nadie declaró.
El criterio no está escondido en el servidor
preparar_escenarios sigue la misma regla que analizar_priorizacion: junta
señales y explica, no devuelve un veredicto. Las señales salen de los pasos
reales de cada caso:
| Señal | Cómo leerla |
|---|---|
| arranca navegando | El que navega se basta solo y puede abrir el escenario. El que no, depende del estado que dejó el anterior |
| página de entrada | Los que entran por la misma página suelen compartir precondición |
| modo de ejecución | No se pueden mezclar: el modo se toma del primer caso y se aplica a todos |
| páginas compartidas | Cuantas más comparten, más sentido tiene correrlos seguidos |
| estabilizado | Sin corrida previa no se agrupa: primero estabilizar_caso |
Con eso propone grupos concretos y dice por qué cada uno. El usuario acepta, corrige o rehace — pero no decide desde cero ni recibe un número sin contexto.
La advertencia que más importa: un escenario donde ningún caso arranca navegando empieza con el navegador en blanco y falla entero. La propuesta lo marca como problema antes de que se cree.
Dos detalles del backend que obligaron a hacerlo así
Los casos se asocian en lote, no de a uno. El serializer de
scenario-test-cases no expone order, que por defecto es 0, y el modelo tiene
un único (escenario, order): agregar el segundo caso de a uno chocaría.
bulk-associate sí calcula el orden y respeta el del arreglo.
Ejecutar no es el paso siguiente automático. crear_plan_y_ejecutar deja el
plan armado y no corre nada salvo que se lo pidan explícitamente: una corrida
real toma un agente y golpea el sistema de verdad. El agente se elige antes de
crear el plan, para no dejar un plan a medio armar si no hay ninguno libre.
Integraciones: un solo par de herramientas para tres destinos
Los tres hacen lo mismo —traer issues, guardarlos como requerimientos, exportar
casos y bugs— pero cada uno con su ruta, y el campo de la credencial se llama
distinto según el endpoint: user, form, auth, userdata, o directamente
sin envoltorio. Ese desorden queda adentro: quien usa las herramientas dice
el destino y ya.
La credencial completa viaja en el cuerpo, no su id, así que primero se busca en
integration/credentials/user/type/<tipo>/ y se manda entera. El password
viene vacío —la API nunca devuelve secretos— y el backend lo resuelve por el id.
Cuando hay varias credenciales del mismo destino, no adivina: devuelve los nombres para que el usuario elija.
Estado verificado contra producción:
| Destino | Estado |
|---|---|
| Jira | ✅ 16 proyectos listados |
| GitLab | ✅ 121 issues traídos |
| Azure | ⚠️ azure_projects responde 406 con las tres credenciales de la organización, mandando la credencial igual que Jira (que sí funciona). Falta descubrir qué espera |
Bugs: sin dirección privilegiada
Cada equipo trabaja distinto. Unos crean el bug acá y después lo suben a Jira;
otros lo reportan en Jira y quieren pegarlo a los casos de acá. Por eso
asociar_bug toma una acción (vincular / desvincular) en vez de haber dos
herramientas, y buscar_bugs responde tanto "qué bugs hay" como "qué bugs tiene
TC-31". Ninguna asume un orden de trabajo.
Datos de prueba: el contexto se arma solo
IzyTesting genera datos, pero pide el contexto armado —historia, caso y pasos— y en la aplicación web eso lo aporta la pantalla: hay que estar parado en el caso y va de a uno. Acá el contexto sale de lo que ya está guardado, así que se puede pedir para cualquier caso o para todos los de un requerimiento, sin navegar.
La respuesta del backend viene con ids de paso; se cruzan con las acciones para que se vea qué valor va con qué paso.
La línea crítica: agrupar, ejecutar a mano, reportar
"Hay una urgencia, agrupá los más críticos y ejecutalos" son cuatro cosas distintas, y el modelo de IzyTesting obliga a hacerlas en orden:
Hito → Conjunto de prueba → Caso vinculado → Ejecución → Paso ejecutadoDos cosas de ese modelo no son evidentes y ordenan todo lo demás:
- Un hito no agrupa casos. Agrupa requerimientos y conjuntos de prueba. Así
que "agrupá estos casos en un hito" es, en realidad, armar un conjunto con
ellos y colgar el conjunto del hito.
agrupar_en_hitohace las tres cosas de una porque separarlas solo trasladaría la confusión a quien la use. - Una ejecución no cuelga de un caso sino de un caso vinculado a un
conjunto (
TestCaseExecution.test_suite_case). Un caso que no está en ningún conjunto no se puede ejecutar. Pasar por el conjunto no es un rodeo nuestro.
Para decidir qué es crítico no hay que inventar nada: analizar_priorizacion ya
devuelve las señales —prioridad, bugs asociados, casos que comparten
requerimiento— y la recomendación la hacen el modelo y el usuario.
Un conjunto no puede mezclar casos Gherkin con casos base. IzyTesting responde 412 sin explicar; la herramienta lo dice con todas las letras.
El hito no decide por vos qué colgarle
Un hito agrupa conjuntos, y cuáles es decisión de quien lo arma. Por eso
agrupar_en_hito toma conjuntos con las claves de los que ya existen —uno o
varios— y solo crea uno nuevo si además le pasás casos y nombre_conjunto.
Las dos cosas se pueden combinar.
La versión anterior siempre creaba uno, y eso dejaba duplicados que después hay que borrar a mano desde la aplicación web. Crear algo que nadie pidió es peor que pedir un dato de más.
Hay dos vocabularios de estado, y no son intercambiables
| Qué | Valores |
|---|---|
| Caso y paso de una ejecución | Pass, Fail, To-do, No-run |
| Conjunto de prueba | Passed, Failed, No run, Not completed |
El del conjunto no se manda nunca: lo calcula el backend contando los casos
(evaluate_test_suite_status). Y ahí está la trampa: guardar Passed en un
caso no falla ni da error — simplemente no lo cuenta ningún contador, ni
pasados ni fallidos ni pendientes, y el conjunto queda evaluado mal sin que nada
lo avise. La pantalla de ejecución solo ofrece To-do, Fail y Pass.
registrar_ejecucion acepta las dos formas en español y corrige también el
plural inglés, que es el error fácil de cometer. Un paso bloqueado se registra
como Fail explicando por qué en las observaciones: la aplicación web no tiene
otro estado.
Quién pone el navegador, otra vez
El mismo reparto que en automatización: el servidor no tiene navegador ni saca capturas. Claude ejecuta los pasos en un navegador de verdad, anota qué vio y saca las capturas; el servidor guarda eso.
Si un paso falla, se sigue con los siguientes. El objetivo de una corrida manual es saber todo lo que anda mal, no frenar en lo primero.
La evidencia se sube después de cerrar el caso
No hay endpoint para "marcá el paso 3 como pasado" mientras se avanza:
case_state graba la ejecución entera de una sola vez. Y está bien que sea así,
porque es el único endpoint de creación de todo el backend que devuelve lo que
creó:
{ "execution_id": 123,
"steps": [{"step_id": 78, "execution_step_id": 456, "status": "Passed"}] }Esos execution_step_id son los que hacen que la captura del paso 3 termine en
el paso 3 y no en un id adivinado. Sin ellos no habría evidencia por paso, solo
un montón de imágenes colgadas del caso.
El cronómetro también lo pone el cliente: duration y start_date los manda
quien ejecuta, el servidor no mide nada.
Sobre las capturas: ocupan cuota de almacenamiento de la organización. Por eso van por paso y a elección, no una por las dudas en cada paso de cada caso.
El bug sale de la misma corrida
Cuando un paso falla, el bug tiene que quedar ligado a esa ejecución, no
suelto en el proyecto. Hay dos endpoints con el mismo cuerpo y solo uno hace
eso, así que crear_bug elige según le pasen o no el conjunto: es la misma
intención de la persona —"reportá esto"— con distinta plomería debajo.
Los dos caminos que ofrece IzyTesting:
| | Cómo |
|---|---|
| Con IA | redactar_bug le pasa a IzyTesting el paso que falló y lo observado, y devuelve el reporte redactado para revisar. Nada se guarda hasta que el usuario apruebe |
| A mano | Si la información ya está, crear_bug directo |
La diferencia queda registrada: IzyTesting marca el bug como ia o izy según
el caso, y eso alimenta sus propios tableros. Por eso redactado_por_ia no es
cosmético — decir que lo escribió una persona cuando lo escribió el modelo
ensucia esa métrica.
redactar_bug consume créditos, y IzyTesting anonimiza las observaciones
antes de mandarlas al modelo y las restituye en la respuesta.
Redactar requerimientos: Claude adapta, IzyTesting redacta
IzyTesting deja escribir historias a mano o con IA. Acá se expone la segunda, y el reparto es el mismo que en automatización, donde el navegador lo pone el cliente:
| Quién | Hace | |---|---| | El cliente MCP (Claude) | Traduce el insumo: un PDF, un correo pegado en el chat, una URL, notas sueltas | | El motor de IzyTesting | Escribe las historias |
Así el resultado sale con el mismo criterio que si se hubiera pedido desde la
aplicación web —no con el de otro modelo—, y a la vez se puede partir de
cualquier cosa. Para una URL, Claude la lee y pasa el contenido como texto,
igual que en generar_casos. De un mismo insumo pueden salir varias historias.
crear_requerimientos(texto o archivos, tipo) → historias propuestas
↓ el usuario elige cuáles
crear_requerimientos(solo_redactar: false, ...) → quedan en IzyTestingEs una sola herramienta con confirmación en el medio, como importar_desde, en
vez de dos: el paso de aprobación es la conversación, no una llamada más.
Por qué guardar necesita el trabajo_id. create_requirement/ saca el
proyecto del log_id que se le manda. Sin él, el backend se queda con un entero
donde espera un objeto Project y responde 500. O sea que el endpoint masivo
solo funciona detrás de un trabajo de redacción real: no es un rodeo nuestro, es
la única forma en que ese endpoint anda. Guardar de a uno sí existe
(create_requirement_manually/), pero no acepta varios ni viene de un análisis.
Lo que no se puede elegir por este camino: el estado queda en Created y la
prioridad la decide el motor, porque así guarda ese endpoint. Si hay que
cambiarlos, se hace después con editar_requerimiento. Lo que sí se elige antes
es el formato —historia clásica o Gherkin—, y eso no se cambia sin volver a
redactar.
El caso que lo hace valer la pena: alguien pide casos de prueba y no tiene
requerimiento donde colgarlos. En vez de trabarse, se le ofrece redactarlo con
lo que ya haya en la conversación, y de ahí sale el story_id que
guardar_casos necesita.
Generar casos: dos pasos con aprobación en el medio
generar_casos(...) → inventario de casos propuestos
↓ el usuario elige cuáles
redactar_casos(...) → detalle completo de los elegidos
↓ el usuario confirma
guardar_casos(...) → quedan en IzyTesting, ligados a un requerimientoVerificado de punta a punta contra producción: una descripción en texto de "restablecer contraseña" produjo 15 casos —con los bordes exactos de vigencia del enlace y de longitud de contraseña—, se aprobaron 3, se redactaron con sus pasos, y quedaron guardados como TC-713, TC-714 y TC-715 ligados al requerimiento indicado.
Los dos son asíncronos y pueden tardar hasta diez minutos. Las herramientas
sondean por hasta cuatro minutos y, si no terminó, devuelven un trabajo_id
para seguir con estado_generacion. Así una espera larga cuesta pocos turnos de
conversación en vez de decenas.
La información puede llegar de tres formas y el usuario no tiene que saber cuál es cuál: el id de un requerimiento, texto libre describiendo qué probar, o rutas de archivos locales. Para una URL, Claude la lee y pasa el contenido como texto.
Sobre el texto libre: el backend no acepta texto suelto — sin archivos ni requerimiento genera desde los objetivos del proyecto e ignora lo que se le mande. Por eso el texto se guarda como un documento temporal y se envía por el camino de archivos, que sí existe.
Sobre el proyecto: IzyTesting genera contra el proyecto activo de la
cuenta, no contra un parámetro. Mandar project en el cuerpo o Project-ID en
la cabecera no lo cambia; se comprobó generando en el proyecto equivocado.
Cambiarlo exige fijar_proyecto, que también cambia lo que la persona ve en la
aplicación web. Por eso las herramientas siempre informan sobre qué proyecto
trabajaron.
Cómo se conversa con esto
Las herramientas están pensadas para que nadie tenga que hablar en claves. Si alguien dice "los casos del login que dejó Ana", el camino natural es:
listar_miembrospara convertir "Ana" en su id.buscar_requerimientosconquery: "login"eid_member.buscar_casosconSearchRequirementdel que corresponda.
Si lo que quiere es dejarle esos ítems a Ana, el mismo id entra en asignar
(que: "requerimiento" o "caso", items con las claves, miembro con el id).
No hace falta pasar el nombre: el backend solo acepta el id numérico.
Ningún paso pide una clave REQ-## de entrada. Pedirla es el último recurso,
no el primero.
Relación con el bot de IzyTesting
Ninguna. Son proyectos separados que dan la casualidad de hablar con el mismo producto:
| | Este servidor | El del bot |
|---|---|---|
| Quién lo usa | Personas, desde su IDE | El agente conversacional |
| Identidad | Token de cada usuario | Vínculo de conversación registrado por el gateway |
| Va contra | El backend de IzyTesting | agent-service |
| Se despliega | En la máquina de cada quien | En el servidor del bot |
No comparten código ni configuración, y actualizar uno no obliga a tocar el otro.
Antes de conectarlo: doctor
Revisa la configuracion y hace una consulta de solo lectura real contra IzyTesting, sin imprimir nunca el token:
cp .env.example .env # y completar los valores
npm run doctorConfiguracion
ok IZYTESTING_URL https://izytesting.tu-dominio.com
ok IZYTESTING_TOKEN eyJhbG….9xQk (312 caracteres)
IZYTESTING_ORG (la de tu cuenta; no hace falta ponerla)
modo solo lectura
ok vence el 26/8/2026, 18:40:11
Conexion
ok organizacion 3f2a… (del perfil)
ok buscar_casos respondio (1 resultado(s) en la pagina 1)
Listo para conectar a un cliente MCP.Detecta lo que mas cuesta diagnosticar despues: token vencido, clave de servicio puesta donde va la del usuario, organizacion equivocada, backend inalcanzable.
De donde sale tu token
En IzyTesting: Integraciones → MCP → Generar clave. Se muestra una sola
vez. Eso es IZYTESTING_TOKEN (izy_mcp_...). No se invalida al volver a
entrar a la web; se revoca desde la misma pantalla.
El JWT de la sesion web sigue sirviendo para probar, pero vence con la sesion y puede dejar de valer antes (423) si el navegador se renueva. No lo uses como credencial permanente.
Desarrollo
npm testLas pruebas ejercitan el servidor por su interfaz real —entran mensajes JSON-RPC, salen mensajes— con el backend de IzyTesting simulado. Cubren el protocolo, que cada llamada viaje con el token del usuario, el modo solo lectura y los errores de configuración y de permisos.
Los contratos que verifican no son inventados: salen de haber ejercitado contra producción las cinco herramientas con las que arrancó el servidor. El backend no es uniforme y cada endpoint tiene su forma:
| Herramienta | Método | Detalle |
|---|---|---|
| buscar_casos | GET | filtros como query params; no acepta sizePage |
| buscar_requerimientos | POST | paginación en la query, filtros en el cuerpo, y las cinco claves siempre (query, priority, id_member, type, status): si falta una, responde 500 |
| ver_requerimiento | GET | por clave |
| listar_miembros | GET | organization/get_members/{org} |
| asignar | POST | traduce claves a ids y pega al endpoint de assigned_to que corresponde: assign_history (id_user), asignar_caso (id_user, no el user_id del Swagger), assign_test_suite (suites_keys son ids), assign-users, o bulk_assign (assigned_to) |
| ver_caso | GET | no hay detalle por clave alcanzable desde afuera; se resuelve buscando por clave y tomando la coincidencia exacta, porque el filtro ya devuelve el caso completo |
| editar_caso | PUT | form-data, no JSON |
| editar_pasos | varios | dos modelos de paso, cuatro endpoints sueltos de un lado y un ViewSet REST del otro; ver abajo |
| todo automation/ | — | la respuesta viene envuelta en {success, error, data}, y paginada adentro de data. Lo desenvuelve client.js: a los bloques les llega el contenido pelado |
| automation/steps/ | GET | pagina con pages, no con page — y page no se ignora: es un campo del filtro, la pantalla del paso. Mandarlo devuelve cero pasos sin ningún error |
| automation/objects/ | GET | filtrar por pantalla es page_id; page ahí sí es el número de página |
Estructura
src/config.js Lee el entorno y detecta configuraciones peligrosas
src/client.js Adaptador hacia IzyTesting. Todo pasa por acá, y acá se
desenvuelve la respuesta de automation/
src/server.js Protocolo MCP sobre stdio
src/tools.js Las siete de consulta y edición; arma la lista completa
src/asignacion.js Asignar un responsable a requerimientos, casos, suites o automatización
src/requerimientos.js Redacción de historias: Claude adapta, IzyTesting redacta
src/ejecucion.js Hitos, conjuntos y ejecución manual con evidencia por paso
src/escenarios.js Escenarios, planes y la corrida real de automatización
src/proyecto.js El proyecto activo, y las búsquedas de casos acotadas a él
src/organizacion.js La organización de la cuenta, si no vino en el entorno
src/generacion.js El ciclo generar → redactar → guardar, y los trabajos
src/datos.js Datos de prueba para uno o varios casos
src/bugs.js Bugs y su vínculo con los casos
src/integraciones.js Jira, Azure y GitLab en las dos direcciones
src/automatizacion.js Páginas, objetos, casos automatizados y estabilización
src/captura.js El contrato de qué mirar en la página, y su validación
src/conversion.js Convertir un caso funcional y priorizar qué automatizar
src/pasos.js Editar los pasos de un caso, en los dos modelos que hay
src/http.js El mismo servidor por HTTP, para clientes en la nubeCada bloque de herramientas vive en su archivo y exporta su propio TOOLS_*;
tools.js los junta en una sola lista. Agregar un bloque no obliga a tocar los
demás.
client.js es un adaptador a propósito. Hoy pega directo contra el backend con
el token del usuario. Si más adelante IzyTesting expone un intermediario que
acepte "traé tu propio token", se reemplaza ese archivo y el resto no se entera.
Sobre Project-ID: de dónde sale
IzyTesting decide el proyecto de dos maneras que no se hablan entre sí:
| Endpoints | De dónde sacan el proyecto |
|---|---|
| automation/* | la cabecera Project-ID — y las ejecuciones la exigen: sin ella responden 400 |
| generación, requerimientos, casos | la ignoran; usan el proyecto activo de la cuenta |
Eso deja dos formas de equivocarse, y las dos ocurrieron:
- Mandarla desde una variable de entorno. Las dos mitades trabajaban en proyectos distintos —los casos generados en uno y su automatización en otro— sin que nada lo avisara.
- No mandarla. Todo lo que la exige deja de funcionar: no se puede crear ni ejecutar nada en automatización.
La salida no es ninguno de los dos extremos: la cabecera espeja el proyecto
activo, resuelto en el momento de cada llamada y solo para automation/. Así
las dos mitades no pueden discrepar, y sigue sin haber ninguna variable de
entorno que cambie el proyecto por atrás. Se cambia con fijar_proyecto, y
queda dicho en la respuesta porque también cambia lo que la persona ve en la
aplicación web.
El alcance por proyecto no es gratis
cases/filter_test_cases/ no se limita al proyecto por su cuenta:
filters &= Q(project__organization=member_instance.organization)
if project_id:
filters &= Q(project_id=project_id)Sin project_id en la query devuelve los casos de toda la organización. No
falla ni avisa: devuelve de más. Y como las claves TC-## se numeran por
proyecto, un TC-31 de otro producto se parece bastante al que uno buscaba.
Medido en producción: 6488 casos sin el filtro contra 13 con él.
Por eso ninguna herramienta llama a ese endpoint directamente — todas pasan por
buscarCasos, que agrega el proyecto activo en un solo lugar. Es el único
endpoint de lectura con este problema: bugs/list_bugs/ y
requirements/filter_requirement_id_member/ toman el proyecto por defecto del
miembro sin que haya que pedírselo.
Sobre X-INTERNAL-SERVICE
El servicio interno del bot manda esa cabecera en cada request. Este servidor no la manda, y es deliberado: es un cliente externo y no debe pedir el trato de uno interno. Si algún endpoint la exigiera, la respuesta correcta no es agregarla acá —cualquiera puede escribir esa cabecera— sino revisar por qué el backend confía en ella.
Pendiente antes de publicar
Publicar @izytesting/izytesting-mcp en npm cuando la clave MCP de Integraciones
esté en el entorno que van a usar los clientes.
