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:
2026-10-07 19:19:51 +02:00
parent 9236cb73c6
commit 80d9bee6e0
8 changed files with 131 additions and 10 deletions

View File

@@ -1,8 +1,9 @@
import { Module } from '@nestjs/common';
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { McpModule } from '@nestjs-mcp/server';
import { TASKSTREAM_API_URL, TaskStreamApi } from './api/taskstream-api';
import { HealthController } from './health.controller';
import { McpHostsMiddleware } from './mcp-hosts.middleware';
import { IssuesResolver } from './tools/issues.resolver';
import { ProjectsResolver } from './tools/projects.resolver';
import { ReleasesResolver } from './tools/releases.resolver';
@@ -23,7 +24,9 @@ token the client sends; without one, only public projects can be read.`;
name: 'taskstream',
version: '1.0.0',
instructions: INSTRUCTIONS,
logging: { level: config.get('MCP_LOG_LEVEL', 'log') },
// The library logs every tool on every request (it builds a server per call):
// only warnings and errors unless asked for more.
logging: { level: config.get('MCP_LOG_LEVEL', 'warn') },
}),
}),
],
@@ -45,4 +48,8 @@ token the client sends; without one, only public projects can be read.`;
UsersResolver,
],
})
export class AppModule {}
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer): void {
consumer.apply(McpHostsMiddleware).forRoutes('mcp');
}
}