From bdc8d4b77837fed2c28f30e88e868730ad63cca8 Mon Sep 17 00:00:00 2001 From: MBorne Date: Tue, 5 May 2026 10:00:08 +0200 Subject: [PATCH 1/3] feat(http): add HTTP_CORS_ALLOWED_ORIGINS to configure allowedOrigins (refs #93) --- README.md | 22 ++++++++++++---------- src/index.ts | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 9bd790c..8ca1cac 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Serveur MCP expérimental fournissant du contexte spatial pour les LLM sur la ba - [Avec Docker en local](#avec-docker-en-local) - [Debug de la version locale](#debug-de-la-version-locale) - [Paramétrage](#paramétrage) + - [Tests](#tests) - [Fonctionnalités (Tools)](#fonctionnalités-tools) - [Utiliser des services spatiaux](#utiliser-des-services-spatiaux) - [Recherche d'informations pour un lieu](#recherche-dinformations-pour-un-lieu) @@ -156,16 +157,17 @@ Pour les tests d'intégration et les tests E2E agent, voir [la documentation dé Pour une utilisation avancée : -| Nom | Description | Valeur par défaut | -| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | -| `TRANSPORT_TYPE` | [Transport](https://mcp-framework.com/docs/Transports/transports-overview) permet de choisir entre "stdio" et "http" | "stdio" | -| `HTTP_HOST` | Adresse d'écoute en mode HTTP. Utile avec Docker pour exposer le service via `0.0.0.0`. | "127.0.0.1" | -| `HTTP_PORT` | Port d'écoute du MCP | 3000 | -| `HTTP_MCP_ENDPOINT` | Chemin d'exposition du MCP en HTTP | "/mcp" | -| `HTTP_TIMEOUT` | Délai maximal, en secondes, pour les appels HTTP sortants vers les services amont IGN. Au-delà, la requête est interrompue et l'outil renvoie une erreur de timeout structurée. | `15` | -| `GPF_WFS_MINISEARCH_OPTIONS` | Chaîne JSON optionnelle pour ajuster les options MiniSearch utilisées par `gpf_wfs_search_types` (`fields`, `combineWith`, `fuzzy`, `boost.namespace`, `boost.name`, `boost.title`, `boost.description`, `boost.properties`, `boost.enums`, `boost.identifierTokens`). | options par défaut de `@ignfab/gpf-schema-store` | -| `LOG_FORMAT` | Le format d'écriture des logs : "json" ou "simple". | "simple" | -| `LOG_LEVEL` | Le niveau d'écriture des logs : ["error", "info", ou "debug"](https://github.com/winstonjs/winston#logging-levels) | "debug" | +| Nom | Description | Valeur par défaut | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | +| `TRANSPORT_TYPE` | [Transport](https://mcp-framework.com/docs/Transports/transports-overview) permet de choisir entre "stdio" et "http" | "stdio" | +| `HTTP_HOST` | Adresse d'écoute en mode HTTP. Utile avec Docker pour exposer le service via `0.0.0.0`. | "127.0.0.1" | +| `HTTP_PORT` | Port d'écoute du MCP | 3000 | +| `HTTP_MCP_ENDPOINT` | Chemin d'exposition du MCP en HTTP | "/mcp" | +| `HTTP_CORS_ALLOWED_ORIGINS` | Permet la [configuration de allowedOrigins pour protection contre les attaques par DNS rebinding](https://www.mcp-framework.com/docs/transports/http-stream#origin-validation-dns-rebinding-protection). Exemple : `HTTP_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://geollm.beta.ign.fr"` | Aucun (warning) | +| `HTTP_TIMEOUT` | Délai maximal, en secondes, pour les appels HTTP sortants vers les services amont IGN. Au-delà, la requête est interrompue et l'outil renvoie une erreur de timeout structurée. | `15` | +| `GPF_WFS_MINISEARCH_OPTIONS` | Chaîne JSON optionnelle pour ajuster les options MiniSearch utilisées par `gpf_wfs_search_types` (`fields`, `combineWith`, `fuzzy`, `boost.namespace`, `boost.name`, `boost.title`, `boost.description`, `boost.properties`, `boost.enums`, `boost.identifierTokens`). | options par défaut de `@ignfab/gpf-schema-store` | +| `LOG_FORMAT` | Le format d'écriture des logs : "json" ou "simple". | "simple" | +| `LOG_LEVEL` | Le niveau d'écriture des logs : ["error", "info", ou "debug"](https://github.com/winstonjs/winston#logging-levels) | "debug" | Exemple : diff --git a/src/index.ts b/src/index.ts index 2a5ea37..0c7a83a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { MCPServer, TransportConfig } from "mcp-framework"; +import { MCPServer, TransportConfig, logger } from "mcp-framework"; import { dirname, join } from "path"; import { fileURLToPath } from "url"; import { readFileSync } from "fs"; @@ -11,6 +11,10 @@ function isTransportType(value: string): value is TransportType { return value === "stdio" || value === "http"; } +/** + * Get the transport type from the environment variable TRANSPORT_TYPE. + * Valid values are "stdio" and "http". If not set, defaults to "stdio". + */ function getTransportType(): TransportType { const transportType = process.env.TRANSPORT_TYPE ?? "stdio"; @@ -21,6 +25,10 @@ function getTransportType(): TransportType { return transportType; } +/** + * Get the HTTP port from the environment variable HTTP_PORT. + * The variable should be a decimal integer between 1 and 65535. If not set, defaults to 3000. + */ function getHttpPort(): number { const rawPort = process.env.HTTP_PORT?.trim(); const invalidHttpPortMessage = `Invalid HTTP_PORT: ${rawPort}. Expected a decimal integer between 1 and 65535.`; @@ -42,6 +50,26 @@ function getHttpPort(): number { return port; } +/** + * Get CORS allowed origins from the environment variable HTTP_CORS_ALLOWED_ORIGINS. + * The variable should be a comma-separated list of origins . + */ +function getCorsAllowedOrigins(): undefined|string[] { + if ( ! process.env.HTTP_CORS_ALLOWED_ORIGINS ) { + logger.warn('Security : HTTP_CORS_ALLOWED_ORIGINS is not set. It is recommended to set this variable to prevent DNS rebinding attacks (e.g., HTTP_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://geollm.beta.ign.fr".'); + return undefined; + } + + const rawOrigins = process.env.HTTP_CORS_ALLOWED_ORIGINS?.trim(); + + if (!rawOrigins) { + return undefined; + } + + return rawOrigins.split(",").map((origin) => origin.trim()); +} + + function buildTransport(transportType: TransportType): TransportConfig { // Handle stdio transport configuration if (transportType === "stdio") { @@ -63,12 +91,16 @@ function buildTransport(transportType: TransportType): TransportConfig { endpoint, cors: { allowOrigin: "*", + allowedOrigins: getCorsAllowedOrigins(), }, host, }, }; } +/** + * Get the version from package.json for the MCP server metadata. + */ function getVersion(): string { const pkgMetadata = JSON.parse( readFileSync(join(__dirname, "../package.json"), "utf-8") From 8d2a8ba0f5f150bb901c28070e3bd6a3f91abf9a Mon Sep 17 00:00:00 2001 From: esgn <5435148+esgn@users.noreply.github.com> Date: Tue, 5 May 2026 11:28:21 +0200 Subject: [PATCH 2/3] feat(cors): improve CORS origins validation and add security warnings for unset or empty variable --- src/index.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index 0c7a83a..a722f94 100644 --- a/src/index.ts +++ b/src/index.ts @@ -55,18 +55,24 @@ function getHttpPort(): number { * The variable should be a comma-separated list of origins . */ function getCorsAllowedOrigins(): undefined|string[] { - if ( ! process.env.HTTP_CORS_ALLOWED_ORIGINS ) { + if (process.env.HTTP_CORS_ALLOWED_ORIGINS === undefined) { logger.warn('Security : HTTP_CORS_ALLOWED_ORIGINS is not set. It is recommended to set this variable to prevent DNS rebinding attacks (e.g., HTTP_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://geollm.beta.ign.fr".'); return undefined; } const rawOrigins = process.env.HTTP_CORS_ALLOWED_ORIGINS?.trim(); - - if (!rawOrigins) { + + const allowedOrigins = rawOrigins + ?.split(",") + .map((origin) => origin.trim()) + .filter(Boolean); + + if (!allowedOrigins || allowedOrigins.length === 0) { + logger.warn('Security : HTTP_CORS_ALLOWED_ORIGINS is empty. It is recommended to set this variable to prevent DNS rebinding attacks (e.g., HTTP_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://geollm.beta.ign.fr".'); return undefined; } - return rawOrigins.split(",").map((origin) => origin.trim()); + return allowedOrigins; } From 846845ffb8083bf7d7ec94673df7c81bb25589da Mon Sep 17 00:00:00 2001 From: esgn <5435148+esgn@users.noreply.github.com> Date: Tue, 5 May 2026 11:37:12 +0200 Subject: [PATCH 3/3] feat: import logger module for enhanced logging functionality --- src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index a722f94..0bc1280 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,8 @@ -import { MCPServer, TransportConfig, logger } from "mcp-framework"; +import { MCPServer, TransportConfig } from "mcp-framework"; import { dirname, join } from "path"; import { fileURLToPath } from "url"; import { readFileSync } from "fs"; +import logger from "./logger.js"; const __dirname = dirname(fileURLToPath(import.meta.url));