Metadata-Version: 2.5
Name: noterp-mcp
Version: 0.1.0
Summary: MCP server con tools para consultar endpoints del sitio NotERP
Project-URL: Homepage, https://gitlab.globalsoftm.com/globalsoft/gs_noterp_mcp
Project-URL: Repository, https://gitlab.globalsoftm.com/globalsoft/gs_noterp_mcp
Project-URL: Issues, https://gitlab.globalsoftm.com/globalsoft/gs_noterp_mcp/-/issues
Author-email: GlobalSoft <rarzate@globalsoft.lat>
Keywords: erp,mcp,model-context-protocol,noterp
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27.0
Requires-Dist: mcp>=1.0.0
Requires-Dist: python-dotenv>=1.0.0
Description-Content-Type: text/markdown

# NotERP MCP

Servidor MCP (Model Context Protocol) en Python con tools para consultar
endpoints del sitio **NotERP**.

## Stack

- [mcp](https://github.com/modelcontextprotocol/python-sdk) (SDK oficial de Anthropic) con `MCPServer`
- `httpx` para peticiones HTTP
- Ejecutado con [uv](https://docs.astral.sh/uv/)

## Requisitos

- [uv](https://docs.astral.sh/uv/getting-started/installation/) (con gestor de Python incluido)

## Configuración

Copia `.env.example` a `.env` y configura las variables:

| Variable             | Descripción                                      | Obligatoria |
| -------------------- | ------------------------------------------------ | ----------- |
| `BASE_URL`           | URL base del sitio NotERP                        | Sí          |
| `TOKEN`              | Token de autenticación                          | No          |
| `SUBSCRIPTION_TOKEN` | Token de suscripción de la plataforma (valida antes de cada tool) | Sí |
| `SUBSCRIPTION_URL`   | URL base del servicio de póliza (sin el token final). Default: `{BASE_URL}/service/servicePoliza.php/validaTokenPoliza` | No |
| `MCP_BEARER_TOKEN`   | Token(s) Bearer exigidos en `streamable-http`/`sse` (varios por comas) | Sí en http/sse |
| `TRANSPORT`          | `stdio` \| `streamable-http` (o alias `http`) \| `sse` (default `stdio`) | No |
| `HOST`               | Host de escucha para `streamable-http`/`sse` (default `127.0.0.1`) | No |
| `PORT`               | Puerto de escucha para `streamable-http`/`sse` (default `8000`) | No        |

## Ejecución

```sh
# stdio (recomendado para opencode, Claude Desktop, etc.)
uv run noterp-mcp

# HTTP (Streamable HTTP)
TRANSPORT=streamable-http uv run noterp-mcp

# SSE
TRANSPORT=sse uv run noterp-mcp
```

También funciona con `uv run python -m noterp_mcp`.

## Configuración en un cliente MCP

Ejemplo para opencode (`opencode.json`):

```json
{
  "mcp": {
    "noterp": {
      "type": "local",
      "command": ["uv", "run", "noterp-mcp"],
      "environment": {
        "BASE_URL": "https://tu-noterp.example.com",
        "TOKEN": "tu-token",
        "SUBSCRIPTION_TOKEN": "tu-token-de-suscripcion"
      }
    }
  }
}
```

## Agregar una tool nueva

1. Crea un archivo en `src/noterp_mcp/tools/` (ej. `noterp_usuarios.py`).
2. Define `register(mcp)` dentro:

   ```python
   from mcp.server import MCPServer
   from noterp_mcp.client import request

   def register(mcp: MCPServer) -> None:
       @mcp.tool()
       def listar_usuarios(activo: bool = True) -> str:
           """Lista usuarios del sitio NotERP."""
           return request("GET", "/api/usuarios", params={"activo": activo})
   ```

3. Regístralo en `src/noterp_mcp/server.py`:

   ```python
   from noterp_mcp.tools import noterp_generic, noterp_usuarios

   TOOL_MODULES = [noterp_generic, noterp_usuarios]
   ```

## Autenticación

El `TOKEN` viaja como parte de la ruta del endpoint. En el path se usa el
placeholder `{TOKEN}` (ej. `/service/serviceListaEmpleados.php/listaEmpleados/{TOKEN}`)
y el cliente lo sustituye por el valor URL-encoded de `TOKEN`. Si el path
incluye `{TOKEN}` y no hay token configurado, la tool devuelve un error claro.
Los headers de auth se gestionan en `auth_headers` de
`src/noterp_mcp/client.py`.

## Validación de suscripción

Antes de ejecutar cualquier tool se consulta el servicio de póliza
(`GET {SUBSCRIPTION_URL}/{SUBSCRIPTION_TOKEN}`). Se considera válida cuando la
respuesta trae `response == "OK"` y `vigente is True`; en cualquier otro caso
(NOK, JSON inválido, error HTTP o de red) la tool se bloquea (fail-closed). Un
resultado válido se cachea 60 segundos. `SUBSCRIPTION_TOKEN` es obligatorio: sin
él el servidor no arranca.

## Autenticación Bearer en web

Con `TRANSPORT=streamable-http` o `TRANSPORT=sse` el servidor exige el header
`Authorization: Bearer <token>` en cada petición; sin token válido responde 401.
Los tokens se configuran en `MCP_BEARER_TOKEN` (varios separados por comas para
rotar con solapamiento). Si no se configura, el servidor no arranca. En `stdio`
no se pide token.

## Tools

| Tool | Endpoint | Descripción |
| ---- | -------- | ----------- |
| `listar_empleados` | `GET /service/serviceListaEmpleados.php/listaEmpleados/{TOKEN}` | Lista empleados de NotERP. Params: `hoja` (int), `all` (0/1), `id_empleado`, `estatus` (ACTIVO/INACTIVO). |
| `get_resource` | `GET {path}` | GET genérico contra una ruta de `BASE_URL`. |
| `ping` | `GET /` | Verifica conectividad con NotERP. |