feat(mcp): ready for a remote server: quiet logs and allowed domains
- Logs default to warnings and errors (MCP_LOG_LEVEL): the library logged every tool on every request (~48 lines per call). - DNS rebinding protection on /mcp, as the MCP spec asks of HTTP servers: the Host header must be in MCP_ALLOWED_HOSTS (default: localhost; compose default mcp.taskstream.pireon.dev,localhost) and a browser Origin, when present, too; otherwise 403 with a JSON-RPC error. Uses the official validators from @modelcontextprotocol/server. - Test for allowed/foreign Host and Origin; .env.example, compose and docs/09-mcp.md document the variables; Word/PDF regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,9 @@ flowchart LR
|
||||
responde con un error que el modelo puede leer y corregir («Cannot move WEB-16 from Backlog to Hecho. Allowed: Por
|
||||
hacer, En curso, Cancelado»), en vez de cortar la llamada.
|
||||
- **Validación**: los parámetros de cada herramienta se validan con zod antes de llamar a la API.
|
||||
- **Solo en su dominio**: `/mcp` rechaza (403) las peticiones cuyo `Host` no esté en `MCP_ALLOWED_HOSTS`, y las de un
|
||||
navegador cuyo `Origin` tampoco lo esté. Es la protección contra _DNS rebinding_ que pide la especificación de MCP;
|
||||
los clientes que llaman desde un servidor no envían `Origin` y solo necesitan el dominio correcto.
|
||||
- **Respuestas compactas**: los listados devuelven lo que el modelo necesita (clave, título, estado, persona…); el
|
||||
detalle completo, con `get_issue` o `get_project`.
|
||||
|
||||
@@ -106,11 +109,12 @@ npx nx test mcp # MCP real contra una API simulada: token r
|
||||
npx nx run mcp:openapi-types # regenerar los tipos tras cambiar la API (apps/api/openapi.json)
|
||||
```
|
||||
|
||||
| Variable | Por defecto | Qué es |
|
||||
| -------------------- | ----------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| `MCP_PORT` | `3010` | Puerto del MCP (no `PORT`: Nx carga el `.env` del workspace, donde `PORT` es el de la API) |
|
||||
| `TASKSTREAM_API_URL` | `http://localhost:3000` | Origen de la API (sus rutas ya llevan `/api`) |
|
||||
| `MCP_LOG_LEVEL` | `log` | `error`, `warn`, `log`, `debug` o `verbose` |
|
||||
| Variable | Por defecto | Qué es |
|
||||
| -------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `MCP_PORT` | `3010` | Puerto del MCP (no `PORT`: Nx carga el `.env` del workspace, donde `PORT` es el de la API) |
|
||||
| `TASKSTREAM_API_URL` | `http://localhost:3000` | Origen de la API (sus rutas ya llevan `/api`) |
|
||||
| `MCP_ALLOWED_HOSTS` | (solo `localhost`) | Dominios en los que responde, separados por comas |
|
||||
| `MCP_LOG_LEVEL` | `warn` | `error`, `warn`, `log`, `debug` o `verbose`. Por encima de `warn`, la librería registra cada herramienta en cada llamada |
|
||||
|
||||
Añadir una herramienta: un método con `@Tool({ name, description, paramsSchema: z.object(…), annotations })` en el
|
||||
resolver de su ámbito (`apps/mcp/src/app/tools`), que llame a la API con `this.api.as(ctx)` (cliente tipado con el
|
||||
@@ -128,5 +132,7 @@ docker compose --profile mcp up -d --build # local: http://localhost:3010/mcp
|
||||
```
|
||||
|
||||
En Dokploy: `COMPOSE_PROFILES=mcp` en las variables del proyecto y un dominio (p. ej. `mcp.taskstream.pireon.dev`) →
|
||||
servicio `mcp`, puerto 3010. Habla con la API por la red interna (`http://task-stream-api:3000`). La imagen usa Node 24,
|
||||
servicio `mcp`, puerto 3010. Si el dominio es otro, ponlo en `MCP_ALLOWED_HOSTS` (por defecto
|
||||
`mcp.taskstream.pireon.dev,localhost`); si no, el MCP responderá 403. Habla con la API por la red interna
|
||||
(`http://task-stream-api:3000`). La imagen usa Node 24,
|
||||
que es lo que pide `@nestjs-mcp/server` 2.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user