Skip to main content
Glama
sjdanielarita

portalv4-context-server

README.md
# PortalV4 Context Server

Servidor local de **Model Context Protocol (MCP)** desarrollado en TypeScript para consultar el contexto técnico de PortalV4 desde clientes MCP. Permite localizar módulos, revisar controladores y modelos PHP, consultar esquemas SQL, generar plantillas de pruebas, exportar colecciones Postman y revisar el estado Git del backend y frontend.

La versión declarada en `package.json` y en el servidor es **0.1.0**. Expone **8 tools y 2 resources**, mediante transporte **stdio**. El cliente inicia un proceso local y se comunica por su entrada y salida estándar. Esta implementación no registra transporte HTTP, prompts ni plantillas de resources.

El servidor se mantiene en un repositorio independiente de PortalV4. La mayoría de las herramientas consultan archivos o metadatos; `export_endpoint_docs` escribe el JSON solicitado y `generate_tests` devuelve código PHP sin guardarlo ni ejecutarlo. Las herramientas que arrancan Laravel ejecutan también su proceso de bootstrap.

## Contenido

- [Prerrequisitos](#prerrequisitos)
- [Instalación y compilación](#instalación-y-compilación)
- [Configuración del entorno](#configuración-del-entorno)
- [Integración con clientes MCP](#integración-con-clientes-mcp)
- [Herramientas disponibles](#herramientas-disponibles)
- [Resources disponibles](#resources-disponibles)
- [Desarrollo y validación](#desarrollo-y-validación)
- [Limitaciones y deuda técnica](#limitaciones-y-deuda-técnica)
- [Resolución de problemas](#resolución-de-problemas)

## Prerrequisitos

| Requisito | Uso |
| --- | --- |
| Node.js | **22 o superior**, según `engines.node`. `.nvmrc` selecciona Node 22 y el Dockerfile usa `node:22-alpine`, en línea con los requisitos del lockfile. |
| pnpm **11.17.0** | Versión fijada en `packageManager`; el repositorio incluye `pnpm-lock.yaml` y `pnpm-workspace.yaml`. |
| Copia local de PortalV4 | Debe contener **ambos** directorios `backend/` y `frontend/`, incluso si sólo se usarán herramientas de una capa. Se valida al iniciar el servidor. |
| PHP CLI en `PATH` | Necesario para `read_database_schema` y `export_endpoint_docs`. La versión debe ser compatible con el backend Laravel; este repositorio no fija una versión de PHP. |
| Backend Laravel preparado | Para esas dos herramientas deben existir `vendor/autoload.php` y `bootstrap/app.php`, y Laravel debe poder arrancar con su configuración real. La instalación de sus dependencias Composer corresponde al backend. |
| Acceso a SQL Server o MySQL | Sólo para `read_database_schema`: conexión Laravel configurada, conectividad y permisos para consultar `INFORMATION_SCHEMA`. Los drivers de Node están incluidos en este proyecto. |
| Git en `PATH` | Sólo para `review_quality_and_git`; `backend/` y `frontend/` deben ser repositorios accesibles por Git. |
| Archivos del estándar LogTrait | Necesarios para los resources, `get_logtrait_context` y `lint_model_audit`; se detallan en [Resources disponibles](#resources-disponibles). |

Para seleccionar la copia de Laravel que usarán las herramientas PHP, consulte [Configuración del entorno](#configuración-del-entorno).

### Dependencias del proyecto

Las versiones siguientes son los rangos declarados en [package.json](package.json); las versiones resueltas se conservan en [pnpm-lock.yaml](pnpm-lock.yaml).

| Dependencia de ejecución | Versión declarada | Función |
| --- | --- | --- |
| `@modelcontextprotocol/server` | `^2.0.0` | Registro de tools/resources y transporte stdio. |
| `mssql` | `^12.7.0` | Consultas de esquema en SQL Server. |
| `mysql2` | `^3.24.2` | Consultas de esquema en MySQL. |
| `zod` | `^4.2.0` | Validación de argumentos de las herramientas. |

Las dependencias de desarrollo son `typescript` (`^5.9.3`), `tsx` (`^4.20.6`), `@types/node` (`^24.10.1`) y `@types/mssql` (`^12.3.0`). No se requiere un proveedor de IA ni una clave API para iniciar este servidor.

## Instalación y compilación

Desde una copia de este repositorio, use la versión de pnpm indicada en `package.json`. Si necesita instalarla mediante npm:

```bash
npm install --global pnpm@11.17.0
```

Compruebe las versiones e instale las dependencias desde la raíz del repositorio:

```bash
node --version
pnpm --version
pnpm install --frozen-lockfile
pnpm check
pnpm build
```

`pnpm-workspace.yaml` incluye este paquete y autoriza el build de `esbuild`, usado por `tsx`. `pnpm build` compila `src/**/*.ts` en `dist/` con configuración estricta de TypeScript, módulos `NodeNext` y destino `ES2022`. El punto de entrada resultante es `dist/index.js`.

Para ejecutar la versión compilada en Linux/WSL, sustituya la ruta por su copia de PortalV4:

```bash
PORTALV4_ROOT=/ruta/absoluta/portalv4 pnpm start
```

El proceso anuncia la raíz detectada en `stderr` y espera mensajes MCP por `stdin`. No abre un puerto ni muestra una interfaz web. El cliente MCP normalmente inicia y administra este proceso; la ejecución manual sirve para comprobar el arranque.

También existe ejecución directa del código TypeScript para desarrollo:

```bash
PORTALV4_ROOT=/ruta/absoluta/portalv4 pnpm dev
```

`dev` ejecuta `tsx src/index.ts`; no tiene modo watch. Tras cambiar el código, reinicie el proceso. Para clientes que ejecutan `dist/index.js`, compile de nuevo y reinicie la conexión MCP.

## Configuración del entorno

El servidor lee variables del entorno del proceso. **No carga automáticamente un archivo `.env` propio**. Configure las variables en el cliente MCP o en el entorno que lanza Node; Laravel resuelve por separado su propia configuración durante el bootstrap.

| Variable | Comportamiento actual |
| --- | --- |
| `PORTALV4_ROOT` | Raíz con `backend/` y `frontend/`. Se normaliza y resuelve con `realpath`. Si se establece y es inválida, el arranque falla sin buscar otra copia. |
| `PORTALV4_BACKEND_PATH` | Override opcional del backend Laravel para `read_database_schema` y `export_endpoint_docs`. Al omitirlo, ambas usan `backend/` bajo la raíz resuelta de PortalV4. La misma ruta se usa como `cwd` y para cargar Laravel; configure una ruta absoluta si necesita el override. |
| `PORTALV4_WSL_DISTRO` | Distribución para traducir rutas POSIX a UNC cuando Node corre en Windows; también se consulta `WSL_DISTRO_NAME` como alternativa. Ejemplo: `Ubuntu-24.04`. |
| `PORTALV4_MAX_CONTROLLER_BYTES` | Límite de lectura para `lint_thin_controller`, en bytes. Predeterminado: `1048576` (1 MiB). Debe representar un entero positivo. No cambia el límite fijo de 1 MiB de modelos y fuentes LogTrait. |

Cuando se omite `PORTALV4_ROOT`, se prueban, en este orden: el directorio actual, `portalv4/` bajo el directorio actual y `portalv4/` junto al repositorio del servidor. Para los clientes de escritorio conviene establecer la raíz explícitamente y usar rutas absolutas.

### Ejecución de las herramientas PHP

Ambas herramientas ejecutan el binario `php` del `PATH` mediante `execFileSync`, con argumentos separados y sin shell. La ruta del backend se normaliza para el sistema que ejecuta Node y se transmite al proceso PHP mediante `PORTALV4_BACKEND_PATH`. Laravel carga `vendor/autoload.php` y `bootstrap/app.php` desde esa misma ruta.

No hay una ruta fija al ejecutable PHP ni al backend. Con `PORTALV4_ROOT` configurado no es necesario añadir `PORTALV4_BACKEND_PATH`: el valor predeterminado es su subdirectorio `backend/`. Si usa el override para otra copia, las herramientas PHP consultarán esa copia, mientras que archivos, Git y LogTrait seguirán usando `PORTALV4_ROOT`.

### Rutas y Windows/WSL

El servidor normaliza rutas POSIX, unidades Windows y UNC de WSL (`\\wsl.localhost\...` o `\\wsl$\...`) según el sistema que ejecuta Node. Dentro de WSL convierte unidades Windows a `/mnt/<unidad>/...`; fuera de WSL, Linux rechaza esas unidades y las rutas UNC de WSL. Cuando `WSL_DISTRO_NAME` está disponible, se rechazan rutas UNC de otra distribución.

Para Windows nativo, instale PHP CLI y añádalo al `PATH` del proceso Node. Para un cliente Windows con PortalV4 dentro de WSL, ejecutar Node mediante `wsl.exe` permite usar PHP y Git de esa distribución; PHP debe estar disponible dentro de WSL.

## Integración con clientes MCP

Compile primero y use `node` directamente con la ruta absoluta a `dist/index.js`. Reemplace todas las rutas de ejemplo por las de su equipo. Si Node no está en el `PATH` del cliente, use la ruta absoluta al ejecutable. `stdout` se reserva para el protocolo MCP; los diagnósticos deben escribirse en `stderr`.

### Claude Desktop

Abra **Settings → Developer → Edit Config**, incorpore la entrada al objeto `mcpServers` existente y reinicie la aplicación. La ubicación documentada del archivo es:

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`.
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`.

Ejemplo para Node ejecutado en un entorno POSIX:

```json
{
  "mcpServers": {
    "portalv4-context": {
      "command": "node",
      "args": ["/ruta/absoluta/portalv4-context-server/dist/index.js"],
      "env": {
        "PORTALV4_ROOT": "/ruta/absoluta/portalv4",
        "PORTALV4_BACKEND_PATH": "/ruta/absoluta/portalv4/backend"
      }
    }
  }
}
```

`PORTALV4_BACKEND_PATH` es opcional en este ejemplo porque coincide con el valor derivado de `PORTALV4_ROOT`. Referencia: [guía oficial de conexión a servidores locales MCP](https://modelcontextprotocol.io/docs/develop/connect-local-servers).

### Cursor

Añada la configuración a `.cursor/mcp.json` en el proyecto o a `~/.cursor/mcp.json` para uso global. La entrada stdio incluye `type: "stdio"`:

```json
{
  "mcpServers": {
    "portalv4-context": {
      "type": "stdio",
      "command": "node",
      "args": ["/ruta/absoluta/portalv4-context-server/dist/index.js"],
      "env": {
        "PORTALV4_ROOT": "/ruta/absoluta/portalv4",
        "PORTALV4_BACKEND_PATH": "/ruta/absoluta/portalv4/backend"
      }
    }
  }
}
```

Recargue la conexión y compruebe las herramientas en la configuración MCP del cliente. Referencia: [documentación oficial de MCP en Cursor](https://cursor.com/docs/mcp). Estas configuraciones describen cómo lanzar el proceso; no constituyen una validación de ejecución en ambos clientes.

### Cliente Windows que ejecuta el servidor dentro de WSL

Ejemplo con rutas de una instalación en WSL. Sustituya las rutas, la distribución y el ejecutable Node por los reales; en WSL puede obtener este último con `command -v node`:

```json
{
  "mcpServers": {
    "portalv4-context": {
      "command": "wsl.exe",
      "args": [
        "--distribution", "Ubuntu-24.04",
        "--exec", "env",
        "PORTALV4_ROOT=/home/sjdarita/proyectos/portalv4",
        "PORTALV4_BACKEND_PATH=/home/sjdarita/proyectos/portalv4/backend",
        "/usr/bin/node",
        "/home/sjdarita/proyectos/portalv4-context-server/dist/index.js"
      ]
    }
  }
}
```

Para Cursor, añada `"type": "stdio"` a esta entrada. El servidor y sus dependencias deben estar instalados y compilados dentro de WSL. `/usr/bin/node` es un ejemplo: una instalación mediante un gestor de versiones puede usar otra ruta.

## Herramientas disponibles

Las herramientas se registran en [src/index.ts](src/index.ts). Los nombres MCP son los indicados abajo; `logtrait-context.ts` y `model-audit.ts` son archivos de implementación, no nombres de tools.

Salvo `generate_tests`, las respuestas se entregan como JSON en `content` de tipo `text` y en `structuredContent`. Los errores capturados por los handlers incluyen `error` e `isError: true`; un hallazgo de lint se comunica mediante `passed`, sin ser necesariamente un error del protocolo.

| Tool | Alcance | Efecto principal |
| --- | --- | --- |
| `get_module_structure` | Backend y frontend | Consulta estructura y destinos; no crea directorios. |
| `lint_thin_controller` | Backend | Inspección estática de un controlador PHP. |
| `read_database_schema` | Backend | Arranca Laravel para obtener configuración y consulta metadatos SQL. |
| `export_endpoint_docs` | Backend | Arranca Laravel, analiza fuentes y escribe una colección Postman. |
| `generate_tests` | Backend | Devuelve una plantilla PHP; no escribe ni ejecuta pruebas. |
| `review_quality_and_git` | Backend y frontend | Ejecuta tres comandos Git de lectura por repositorio. |
| `get_logtrait_context` | Backend o frontend | Lee el estándar de la capa y la fuente backend de LogTrait. |
| `lint_model_audit` | Backend | Inspección estática del modelo y lectura del estándar backend. |

### `get_module_structure`

Argumento obligatorio: `moduleName`, un segmento PascalCase que cumpla `^[A-Z][A-Za-z0-9]*$`.

```json
{ "moduleName": "WebCoosajo" }
```

Busca directorios bajo `Modules/` y `app/Http/Controllers/` de ambas capas, respetando el nombre real del disco y comparando sin distinguir mayúsculas/minúsculas. Prioriza `Modules` si existe; conserva la arquitectura clásica si es la única encontrada y propone destinos en `Modules` para un módulo ausente.

Devuelve `requestedModule`, `canonicalModule`, `selectedArchitecture`, indicadores `detected`, `selectionReason`, `destinations`, `developmentStandards` y `notes`. Cada destino incluye `absolutePath`, `portablePath`, `exists` y `kind`. La presencia de una ruta propuesta no implica que exista: compruebe `exists`.

### `lint_thin_controller`

Argumento obligatorio: `controllerPath`, ruta a un `.php` existente, absoluta o relativa a `backend/`. Resuelve enlaces simbólicos y rechaza archivos fuera del backend; el tamaño máximo se configura con `PORTALV4_MAX_CONTROLLER_BYTES`.

```json
{ "controllerPath": "Modules/WebCoosajo/Controllers/BeneficioController.php" }
```

Enmascara comentarios y cadenas y usa expresiones regulares para detectar:

- `ELOQUENT_STATIC_CALL`: llamadas estáticas a modelos reconocidos por importación o nombre calificado.
- `ELOQUENT_MUTATION`: llamadas como `save`, `update`, `delete`, `restore`, `increment`, `decrement`, `touch` o `push`.
- `DB_FACADE_CALL`: acceso mediante `DB::`.

Devuelve `controllerPath`, `portablePath`, `passed`, `findingCount`, `findings` y `requiredAction`. Los hallazgos incluyen regla, línea, columna, coincidencia y mensaje. No ejecuta PHP ni corrige el controlador; las expresiones regulares no sustituyen un parser ni resuelven todos los alias o tipos de objetos.

### `read_database_schema`

Argumentos:

- `table_name` **obligatorio**: cadena de hasta 128 caracteres; `""` lista las tablas base disponibles.
- `connection_name` opcional: conexión Laravel; al omitirla se usa `database.default`. Debe cumplir `^[A-Za-z][A-Za-z0-9_-]*$`.

```json
{ "table_name": "Agencia", "connection_name": "sqlsrv" }
```

```json
{ "table_name": "" }
```

Arranca Laravel mediante `php -r` para resolver la configuración efectiva. Node conecta usando `mssql` para el driver `sqlsrv` o `mysql2/promise` para `mysql`; otros drivers se rechazan. Ejecuta consultas fijas de lectura sobre `INFORMATION_SCHEMA` y parametriza el nombre de tabla. No admite SQL arbitrario ni recibe credenciales como argumentos.

Devuelve `connectionName`, `driver` y `mode`; en modo `tables`, `tableCount` y `tables`; en modo `columns`, `tableName`, `columnCount` y `columns`. Las columnas incluyen esquema, nombre, posición, tipo, longitud, nulabilidad y una clasificación de llave (`PRIMARY KEY`, `FOREIGN KEY` o `UNIQUE`, cuando corresponde). No proporciona un inventario completo de índices ni destinos de claves foráneas. En SQL Server el filtro es por nombre de tabla y puede devolver varios esquemas con ese mismo nombre.

La conexión se cierra al terminar. La resolución de configuración PHP tiene timeout de 15 segundos; SQL Server usa 15 segundos para conectar y 30 para consultar; MySQL fija 15 segundos para conectar. Ambas herramientas PHP usan el backend seleccionado según [Configuración del entorno](#configuración-del-entorno).

### `export_endpoint_docs`

| Argumento | Obligatorio | Comportamiento |
| --- | --- | --- |
| `output_path` | Sí | Ruta absoluta a un `.json`, de 1 a 4096 caracteres, con directorio padre existente. |
| `prefix` | No | Prefijo de URI, hasta 255 caracteres; se eliminan las barras iniciales. |
| `module` | No | Compara, sin distinguir mayúsculas/minúsculas, el módulo extraído de la acción `Modules\<Nombre>\...`. |
| `route_file` | No | Filtro estricto por archivo PHP de registro: ruta relativa al backend configurado, absoluta dentro de él, o nombre de archivo si coincide con una única fuente registrada. |
| `search_pattern` | No | Expresión regular con flag `i` sobre URI, acción o nombre de ruta. |
| `overwrite` | No | `false` por defecto; `true` permite reemplazar un archivo existente, con autorización expresa del usuario. |

```json
{
  "output_path": "/ruta/absoluta/exportaciones/webcoosajo.postman.json",
  "prefix": "web-coosajo/",
  "module": "WebCoosajo",
  "route_file": "Modules/WebCoosajo/Routes/WebCoosajo.php",
  "overwrite": false
}
```

Obtiene las rutas registradas mediante bootstrap Laravel y reflexión PHP. Genera una **Postman Collection v2.1.0**, agrupada por el primer segmento de URI y con variables para parámetros de ruta. Omite `HEAD` y `OPTIONS` cuando quedan otros métodos; si son los únicos, conserva el valor de métodos original como alternativa.

La colección define `base_url` con valor inicial `http://localhost`; agregue o configure en Postman las variables referenciadas por las solicitudes, como `token`, `_Colaborador`, `_Token` y los parámetros de ruta. Incluye `Accept`, `Content-Type` para POST/PUT/PATCH y `Authorization: Bearer {{token}}` cuando reconoce middleware `auth:api`, `auth:passport` o que contiene `passport`.

El análisis de fuentes sólo busca menciones textuales de `_Colaborador` y `_Token` en el controlador y sus dependencias reflejadas; puede colocarlas en el cuerpo JSON de POST/PUT/PATCH. **No extrae las reglas generales de FormRequest ni reconstruye todas las cabeceras, cuerpos, respuestas o requisitos de autenticación.** Revise la colección contra cada endpoint antes de compartirla como contrato de API.

Rechaza un destino existente que sea enlace simbólico o no sea un archivo regular; no crea el directorio padre ni usa una ruta de salida predeterminada. El JSON completo queda en el archivo. La respuesta contiene `exported`, `output_path`, `overwritten`, `prefix`, `route_count`, `request_count`, `byte_count` y `format`.

Sin filtros se procesan todas las rutas. `route_file` identifica el archivo que declara cada ruta durante su registro, antes de realizar reflexión y análisis de fuentes de los endpoints seleccionados. La coincidencia es por archivo exacto, no por subcadena ni por el archivo del controlador. Los demás filtros se aplican conjuntamente al conjunto seleccionado. Un nombre ambiguo, un archivo inválido o un filtro vacío producen un error; una ruta válida sin endpoints registrados produce una colección vacía.

Para identificar las fuentes, el proceso PHP temporal registra las rutas sin usar la caché de rutas de Laravel. No elimina ni modifica archivos de caché del backend. Se ejecuta el bootstrap completo para conservar prefijos y middleware de los grupos, pero sólo los endpoints del archivo seleccionado pasan a reflexión y exportación. Si una implementación de router no permite identificar las fuentes, la herramienta devuelve un error en lugar de exportar rutas sin atribución. El timeout de extracción PHP sigue siendo de 30 segundos.

### `generate_tests`

Argumentos obligatorios: `test_name` (PascalCase, `^[A-Z][A-Za-z0-9]*$`) y `type` (`Feature` o `Unit`). `module_name` es opcional y usa el mismo patrón de nombre.

```json
{
  "test_name": "BeneficioControllerTest",
  "type": "Feature",
  "module_name": "WebCoosajo"
}
```

Devuelve texto PHP con namespace `Tests\<type>` o `Modules\<module_name>\Tests\<type>`, herencia de `Tests\TestCase`, importación y uso de `DatabaseTransactions` y un método `test_example()` vacío. No usa `RefreshDatabase`. No añade aserciones, no guarda archivos y no ejecuta pruebas; el equipo debe completar y revisar la plantilla antes de utilizarla.

### `review_quality_and_git`

No requiere argumentos. Los campos adicionales heredados se aceptan y se ignoran.

```json
{}
```

Inspecciona `backend/` y `frontend/` bajo la raíz resuelta ejecutando exclusivamente:

```bash
git status --porcelain
git rev-parse --abbrev-ref HEAD
git diff --stat
```

Usa `GIT_OPTIONAL_LOCKS=0`, `GIT_PAGER=cat` y timeout de 10 segundos por comando. Devuelve `audit_mode: "passive_read_only"`, `audit_completed`, `git_writes_executed: false` e informes por repositorio con rama, archivos staged/unstaged/untracked, resumen del diff, observaciones y sugerencias.

Valida ramas con prefijos `feature/`, `bugfix/`, `hotfix/`, `release/` o `chore/` y segmentos en kebab-case. Puede sugerir una rama y un mensaje Conventional Commits a partir de los archivos y la rama actuales; no ejecuta operaciones de escritura en Git. `git diff --stat` resume cambios rastreados sin preparar; staged y untracked se identifican por `status --porcelain`. La revisión de calidad se basa en metadatos y nombres de archivos potencialmente sensibles, no en el contenido del diff.

### `get_logtrait_context`

Argumento obligatorio: `layer`, exactamente `backend` o `frontend`; rechaza campos adicionales.

```json
{ "layer": "backend" }
```

Lee en cada llamada la sección aprobada de `.github/copilot-instructions.md` de la capa y `backend/app/Traits/LogTrait.php`, sin caché ni copia de respaldo del estándar. Devuelve `layer`, `resourceUri`, `policyMarkdown`, `traitSource` y `sources`; cada fuente incluye `path`, `startLine`, `endLine` y SHA-256 del texto devuelto.

Resuelve enlaces simbólicos, limita las lecturas a la capa correspondiente y admite archivos de hasta 1 MiB. Normaliza a LF la sección Markdown. Si falta una fuente o no hay exactamente una sección con el título requerido, devuelve un error explícito. No ejecuta PHP ni consulta la base de datos.

### `lint_model_audit`

Argumentos obligatorios: `modelPath` (archivo `.php` existente, absoluto o relativo a `backend/`) y `mode` (`new` o `legacy`). Rechaza campos adicionales.

```json
{
  "modelPath": "Modules/AdministracionCarteras/Models/Perfil.php",
  "mode": "new"
}
```

Lee el modelo dentro del backend, hasta 1 MiB, y consulta el estándar backend de LogTrait. Inspecciona clases, importaciones, traits y métodos mediante un analizador léxico limitado. No ejecuta PHP, no arranca Laravel ni modifica archivos.

Devuelve `modelPath`, `mode`, `verification: "static"`, `status`, `passed`, `hasLogTrait`, `findings`, `manualReview`, `standard` y `sources`. `hasLogTrait` indica uso directo reconocido; no prueba su ausencia en una herencia o composición no resuelta. Los hallazgos incluyen regla, severidad, línea, columna y mensaje, sin copiar los valores del modelo.

| Regla | Interpretación |
| --- | --- |
| `LOGTRAIT_MISSING` | Ausencia reconocida del trait: error en `new`; advertencia y revisión en `legacy`. |
| `MODEL_BOOT_PARENT_MISSING` | `boot()` propio sin llamada reconocida a `parent::boot()`: error con padre Eloquent directo y análisis léxico resuelto; revisión en otros casos. |
| `LOGTRAIT_BOOT_OVERRIDE` | Requiere revisar la redefinición de `bootLogTrait()`. |
| `LOGTRAIT_LOGBD_OVERRIDE` | Requiere revisar la redefinición de `LogBD()`. |
| `LOGTRAIT_UNSUPPORTED_FILTER_CONFIG` | Advierte que `$logAttributes` y `$ignoreFields` no configuran exclusiones de LogTrait. |
| `MODEL_AUDIT_UNRESOLVED` | Sintaxis, herencia o composición no resuelta; requiere revisión manual. |

| `status` | `passed` | Significado |
| --- | --- | --- |
| `compliant` | `true` | Supera las comprobaciones estáticas aplicables. |
| `non_compliant` | `false` | Existe un error determinista; prevalece sobre los casos de revisión. |
| `needs_review` | `null` | El análisis no permite aprobar, o falta el trait en modo `legacy`. |

Reconoce herencia directa de `Illuminate\Database\Eloquent\Model`, importaciones simples y múltiples con alias y uso directo o plenamente calificado de `App\Traits\LogTrait`. Ignora comentarios y literales. Importaciones agrupadas, atributos PHP, heredoc/nowdoc, padres personalizados, otros traits y adaptaciones requieren revisión.

`passed: true` no certifica auditoría en ejecución. La revisión manual debe verificar identidad y cabeceras, atributos sensibles, manejo de transacciones y fallos, escrituras sin eventos y registros manuales duplicados. Reconocer `parent::boot()` tampoco demuestra su ejecución en todas las ramas.

## Resources disponibles

| Nombre registrado | URI | Contenido |
| --- | --- | --- |
| `logtrait_standard_backend` | `portalv4://standards/backend/logtrait` | Sección aprobada backend, fuente de LogTrait y referencias verificables. |
| `logtrait_standard_frontend` | `portalv4://standards/frontend/logtrait` | Sección aprobada frontend, fuente backend de LogTrait y referencias verificables. |

Ambos usan MIME `text/markdown`, leen las fuentes en cada consulta y comparten implementación con `get_logtrait_context`. Su registro no comprueba que las fuentes existan; la comprobación ocurre al leerlos. Se requieren estos archivos:

```text
portalv4/
├── backend/
│   ├── .github/copilot-instructions.md
│   └── app/Traits/LogTrait.php
└── frontend/
    └── .github/copilot-instructions.md
```

En cada documento de instrucciones debe existir **exactamente una** cabecera de tercer nivel con el título literal correspondiente:

```markdown
### Auditoría de modelos mediante LogTrait
```

```markdown
### Contexto de auditoría en llamadas al backend
```

La sección termina ante la siguiente cabecera de nivel 1, 2 o 3. Los hashes describen el texto devuelto, no necesariamente el archivo completo de instrucciones. El estándar normativo se mantiene en PortalV4; este README documenta cómo consultarlo.

## Desarrollo y validación

### Scripts disponibles

| Comando | Script real | Resultado |
| --- | --- | --- |
| `pnpm check` | `tsc -p tsconfig.json --noEmit` | Comprobación de tipos sin generar JavaScript. |
| `pnpm build` | `tsc -p tsconfig.json` | Compilación a `dist/`. |
| `pnpm dev` | `tsx src/index.ts` | Ejecución directa del servidor TypeScript. |
| `pnpm start` | `node dist/index.js` | Ejecución del servidor compilado. |

No hay scripts `test`, `lint` ni `watch` definidos en `package.json`. `check` comprueba tipos; no demuestra conectividad SQL, bootstrap Laravel ni funcionamiento en un cliente de escritorio.

### Organización del código

| Archivo | Responsabilidad |
| --- | --- |
| [src/index.ts](src/index.ts) | Arranque, localización y normalización de rutas, registro MCP, estructura de módulos, lint de controladores, consultas SQL, exportación Postman, plantillas PHP y revisión Git. |
| [src/logtrait-context.ts](src/logtrait-context.ts) | Lectura acotada de fuentes, extracción de secciones, referencias SHA-256 y resources LogTrait. |
| [src/model-audit.ts](src/model-audit.ts) | Análisis estático de modelos y construcción del informe de auditoría. |
| [tsconfig.json](tsconfig.json) | Compilación estricta de `src/` a `dist/`. |
| [.nvmrc](.nvmrc) | Selección de Node 22 para quienes usan nvm. |
| [pnpm-workspace.yaml](pnpm-workspace.yaml) | Workspace del paquete y autorización de build de `esbuild`. |
| [Dockerfile](Dockerfile) | Imagen de ejecución; limitaciones descritas abajo. |

### Comprobación desde un cliente MCP

Después de `pnpm check` y `pnpm build`, conecte el cliente y compruebe:

1. `tools/list` devuelve las 8 herramientas de este catálogo.
2. `resources/list` devuelve los 2 resources LogTrait.
3. Una llamada a `get_module_structure` refleja las rutas de su copia de PortalV4.
4. La lectura de un resource o `get_logtrait_context` devuelve fuentes de esa copia, si los archivos requeridos están disponibles.

Use esta comprobación para distinguir el arranque del servidor de la disponibilidad real de cada herramienta. Las herramientas PHP y SQL requieren sus prerrequisitos particulares.

El flujo indicado por las instrucciones del servidor es consultar primero `get_module_structure`, leer el estándar LogTrait de la capa, ejecutar los lints que correspondan y revisar Git pasivamente. Para modelos backend nuevos auditables, `passed` debe ser `true` antes de concluir la verificación estática, además de atender `manualReview`.

## Limitaciones y deuda técnica

- **Selección explícita de otro backend:** `PORTALV4_BACKEND_PATH` prevalece para ambas herramientas PHP. Si apunta a otra copia, archivos/Git/LogTrait y PHP consultarán copias diferentes; omítalo cuando baste `PORTALV4_ROOT/backend`.
- **Atribución de rutas:** el filtro depende del registro mediante el router Laravel en el proceso temporal. No atribuye rutas sin una fuente PHP identificable dentro del backend ni usa archivos de controladores como sustituto. Los nombres de archivo ambiguos requieren una ruta completa.
- **Exportación de contratos parcial:** no se extraen las reglas generales de FormRequest ni se analizan las fuentes de middleware para completar contratos. La búsqueda textual de `_Colaborador`/`_Token` puede confundir cabeceras con cuerpo. Las variables referenciadas, salvo `base_url`, no se declaran en la colección; deben configurarse al usarla.
- **Código contextual sin integración completa:** `RouteRegistration` no se utiliza; `requiredHeaders`, `signedUrlRequired` y `notes` no alimentan la colección. La reflexión produce dependencias con la clave `class`, mientras el tipo TypeScript espera `className` y se convierte con un cast. Las lecturas de fuentes fallidas se ignoran, por lo que una exportación exitosa puede estar incompleta.
- **Bootstrap Laravel:** las consultas SQL emitidas por el MCP son de lectura, pero resolver configuración y rutas ejecuta Laravel y sus proveedores de servicio. El servidor no limita el comportamiento interno de ese bootstrap.
- **Docker incompleto para el catálogo:** el Dockerfile instala Git y dependencias Node, compila y fija `PORTALV4_ROOT=/app/portalv4`, pero no instala PHP ni Composer ni provisiona una copia de PortalV4. Instala pnpm global sin fijar versión explícita y usa `--no-frozen-lockfile`. No constituye por sí solo un entorno listo para todas las herramientas.
- **Alcance de la validación:** los lints PHP son estáticos y la revisión Git sólo inspecciona metadatos. No hay una suite de pruebas ni un script de tests en el repositorio; la compilación no sustituye verificación funcional en Laravel, SQL Server/MySQL o los clientes MCP.

## Resolución de problemas

| Síntoma | Comprobación o acción |
| --- | --- |
| Error de arranque: no se encontró PortalV4 | Configure `PORTALV4_ROOT` con una raíz accesible que contenga `backend/` y `frontend/`. Revise `stderr` del proceso. |
| No se encuentra `dist/index.js` | Ejecute `pnpm build` y revise la ruta absoluta configurada en `args`. |
| El cliente no encuentra Node | Use la ruta absoluta al ejecutable y confirme su versión desde el entorno que lanza el servidor. |
| Error de traducción POSIX/UNC en Windows | Configure `PORTALV4_WSL_DISTRO`, use una ruta UNC válida o ejecute el servidor dentro de WSL. |
| Laravel no pudo resolver la conexión | Confirme PHP en `PATH`, dependencias y bootstrap del backend configurado; revise el nombre de conexión y la configuración efectiva de Laravel. |
| Laravel no pudo exportar la lista de rutas | Confirme PHP en `PATH` y el backend seleccionado por `PORTALV4_BACKEND_PATH` o, por defecto, `PORTALV4_ROOT/backend`. |
| `route_file` no coincide o es ambiguo | Use la ruta relativa completa, como `routes/api.php` o `Modules/WebCoosajo/Routes/WebCoosajo.php`, o una ruta absoluta dentro del backend configurado. |
| Error de driver o conexión SQL | Sólo se admiten `sqlsrv` y `mysql`. Revise conectividad, permisos y configuración Laravel; en SQL Server se usan `encrypt` y `trust_server_certificate` de esa configuración. |
| Error leyendo el estándar LogTrait | Compruebe los archivos, el límite de 1 MiB y el título exacto y único de la sección requerida. |
| `lint_model_audit` devuelve `passed: null` | Atienda `findings` y `manualReview`; el resultado requiere revisión y no es una aprobación estática. |
| Exportación rechazada | Use una ruta absoluta `.json`, con padre existente y destino regular. Si ya existe, sólo use `overwrite: true` con autorización expresa. |
| Faltan cambios en el resumen Git | Consulte `changed_files` y `status_porcelain`; el diff estadístico no incluye staged ni untracked. |
| El servidor arranca pero parece inactivo | En stdio espera mensajes del cliente; conecte un cliente MCP para consultar tools/resources. |

Para diagnosticar una conexión, consulte los logs MCP del cliente y la salida `stderr` del servidor. Evite imprimir diagnósticos en `stdout`, porque puede interrumpir el protocolo.