Skip to main content
Glama

Filesystem MCP Server

index.ts15.8 kB
/** * @fileoverview Loads, validates, and exports application configuration. * This module centralizes configuration management, sourcing values from * environment variables and `package.json`. It uses Zod for schema validation * to ensure type safety and correctness of configuration parameters. * * Key responsibilities: * - Load environment variables from a `.env` file. * - Read `package.json` for default server name and version. * - Define a Zod schema for all expected environment variables. * - Validate environment variables against the schema. * - Construct and export a comprehensive `config` object. * - Export individual configuration values like `logLevel` and `environment` for convenience. * * @module src/config/index */ import dotenv from "dotenv"; import { existsSync, mkdirSync, readFileSync, statSync } from "fs"; import path, { dirname, join } from "path"; import { fileURLToPath } from "url"; import { z } from "zod"; dotenv.config(); // --- Determine Project Root --- /** * Finds the project root directory by searching upwards for package.json. * @param startDir The directory to start searching from. * @returns The absolute path to the project root, or throws an error if not found. */ const findProjectRoot = (startDir: string): string => { let currentDir = startDir; while (true) { const packageJsonPath = join(currentDir, "package.json"); if (existsSync(packageJsonPath)) { return currentDir; } const parentDir = dirname(currentDir); if (parentDir === currentDir) { // Reached the root of the filesystem without finding package.json throw new Error( `Could not find project root (package.json) starting from ${startDir}`, ); } currentDir = parentDir; } }; let projectRoot: string; try { // For ESM, __dirname is not available directly. // import.meta.url gives the URL of the current module. const currentModuleDir = dirname(fileURLToPath(import.meta.url)); projectRoot = findProjectRoot(currentModuleDir); } catch (error: any) { console.error(`FATAL: Error determining project root: ${error.message}`); // Fallback to process.cwd() if project root cannot be determined. // This might happen in unusual execution environments. projectRoot = process.cwd(); console.warn( `Warning: Using process.cwd() (${projectRoot}) as fallback project root.`, ); } // --- End Determine Project Root --- const pkgPath = join(projectRoot, "package.json"); // Use determined projectRoot let pkg = { name: "mcp-ts-template", version: "0.0.0" }; try { pkg = JSON.parse(readFileSync(pkgPath, "utf-8")); } catch (error) { if (process.stdout.isTTY) { console.error( "Warning: Could not read package.json for default config values. Using hardcoded defaults.", error, ); } } /** * Zod schema for validating environment variables. * Provides type safety, validation, defaults, and clear error messages. * @private */ const EnvSchema = z.object({ /** Optional. The desired name for the MCP server. Defaults to `package.json` name. */ MCP_SERVER_NAME: z.string().optional(), /** Optional. The version of the MCP server. Defaults to `package.json` version. */ MCP_SERVER_VERSION: z.string().optional(), /** Minimum logging level. See `McpLogLevel` in logger utility. Default: "debug". */ MCP_LOG_LEVEL: z.string().default("debug"), /** Directory for log files. Defaults to "logs" in project root. */ LOGS_DIR: z.string().default(path.join(projectRoot, "logs")), /** Runtime environment (e.g., "development", "production"). Default: "development". */ NODE_ENV: z.string().default("development"), /** MCP communication transport ("stdio" or "http"). Default: "stdio". */ MCP_TRANSPORT_TYPE: z.enum(["stdio", "http"]).default("stdio"), /** HTTP server port (if MCP_TRANSPORT_TYPE is "http"). Default: 3010. */ MCP_HTTP_PORT: z.coerce.number().int().positive().default(3010), /** HTTP server host (if MCP_TRANSPORT_TYPE is "http"). Default: "127.0.0.1". */ MCP_HTTP_HOST: z.string().default("127.0.0.1"), /** Optional. Comma-separated allowed origins for CORS (HTTP transport). */ MCP_ALLOWED_ORIGINS: z.string().optional(), /** Optional. Secret key (min 32 chars) for auth tokens (HTTP transport). CRITICAL for production. */ MCP_AUTH_SECRET_KEY: z .string() .min( 32, "MCP_AUTH_SECRET_KEY must be at least 32 characters long for security reasons.", ) .optional(), /** Optional. Application URL for OpenRouter integration. */ OPENROUTER_APP_URL: z .string() .url("OPENROUTER_APP_URL must be a valid URL (e.g., http://localhost:3000)") .optional(), /** Optional. Application name for OpenRouter. Defaults to MCP_SERVER_NAME or package name. */ OPENROUTER_APP_NAME: z.string().optional(), /** Optional. API key for OpenRouter services. */ OPENROUTER_API_KEY: z.string().optional(), /** Default LLM model. Default: "google/gemini-2.5-flash-preview:thinking". */ LLM_DEFAULT_MODEL: z .string() .default("google/gemini-2.5-flash-preview-05-20"), /** Optional. Default LLM temperature (0.0-2.0). */ LLM_DEFAULT_TEMPERATURE: z.coerce.number().min(0).max(2).optional(), /** Optional. Default LLM top_p (0.0-1.0). */ LLM_DEFAULT_TOP_P: z.coerce.number().min(0).max(1).optional(), /** Optional. Default LLM max tokens (positive integer). */ LLM_DEFAULT_MAX_TOKENS: z.coerce.number().int().positive().optional(), /** Optional. Default LLM top_k (non-negative integer). */ LLM_DEFAULT_TOP_K: z.coerce.number().int().nonnegative().optional(), /** Optional. Default LLM min_p (0.0-1.0). */ LLM_DEFAULT_MIN_P: z.coerce.number().min(0).max(1).optional(), /** Optional. API key for Google Gemini services. */ GEMINI_API_KEY: z.string().optional(), /** Optional. OAuth provider authorization endpoint URL. */ OAUTH_PROXY_AUTHORIZATION_URL: z .string() .url("OAUTH_PROXY_AUTHORIZATION_URL must be a valid URL.") .optional(), /** Optional. OAuth provider token endpoint URL. */ OAUTH_PROXY_TOKEN_URL: z .string() .url("OAUTH_PROXY_TOKEN_URL must be a valid URL.") .optional(), /** Optional. OAuth provider revocation endpoint URL. */ OAUTH_PROXY_REVOCATION_URL: z .string() .url("OAUTH_PROXY_REVOCATION_URL must be a valid URL.") .optional(), /** Optional. OAuth provider issuer URL. */ OAUTH_PROXY_ISSUER_URL: z .string() .url("OAUTH_PROXY_ISSUER_URL must be a valid URL.") .optional(), /** Optional. OAuth service documentation URL. */ OAUTH_PROXY_SERVICE_DOCUMENTATION_URL: z .string() .url("OAUTH_PROXY_SERVICE_DOCUMENTATION_URL must be a valid URL.") .optional(), /** Optional. Comma-separated default OAuth client redirect URIs. */ OAUTH_PROXY_DEFAULT_CLIENT_REDIRECT_URIS: z.string().optional(), /** Optional. Base directory for all filesystem operations. If set, tools cannot access paths outside this directory. Can be an absolute path or relative to the project root. */ FS_BASE_DIRECTORY: z.string().optional(), }); const parsedEnv = EnvSchema.safeParse(process.env); if (!parsedEnv.success) { if (process.stdout.isTTY) { console.error( "❌ Invalid environment variables found:", parsedEnv.error.flatten().fieldErrors, ); } // Consider throwing an error in production for critical misconfigurations. } let env = parsedEnv.success ? parsedEnv.data : EnvSchema.parse({}); // Resolve FS_BASE_DIRECTORY if it's relative let resolvedFsBaseDirectory: string | undefined = env.FS_BASE_DIRECTORY; if (env.FS_BASE_DIRECTORY && !path.isAbsolute(env.FS_BASE_DIRECTORY)) { resolvedFsBaseDirectory = path.resolve(projectRoot, env.FS_BASE_DIRECTORY); if (process.stdout.isTTY) { console.log( `Info: Relative FS_BASE_DIRECTORY "${env.FS_BASE_DIRECTORY}" resolved to "${resolvedFsBaseDirectory}".` ); } } if (process.stdout.isTTY) { if (resolvedFsBaseDirectory) { // Ensure the resolved directory exists, or attempt to create it. // This is a good place to also check if it's a directory. try { if (!existsSync(resolvedFsBaseDirectory)) { mkdirSync(resolvedFsBaseDirectory, { recursive: true }); console.log(`Info: Created FS_BASE_DIRECTORY at "${resolvedFsBaseDirectory}".`); } else { const stats = statSync(resolvedFsBaseDirectory); if (!stats.isDirectory()) { console.error(`Error: FS_BASE_DIRECTORY "${resolvedFsBaseDirectory}" exists but is not a directory. Restriction will not be applied.`); resolvedFsBaseDirectory = undefined; // Disable restriction if path is invalid } } if (resolvedFsBaseDirectory) { console.log( `Info: Filesystem operations will be restricted to base directory: ${resolvedFsBaseDirectory}` ); } } catch (error: any) { console.error(`Error processing FS_BASE_DIRECTORY "${resolvedFsBaseDirectory}": ${error.message}. Restriction will not be applied.`); resolvedFsBaseDirectory = undefined; // Disable restriction on error } } else { console.warn( "Warning: FS_BASE_DIRECTORY is not set. Filesystem operations will not be restricted to a base directory. This is a potential security risk." ); } } // --- Directory Ensurance Function --- /** * Ensures a directory exists and is within the project root. * @param dirPath The desired path for the directory (can be relative or absolute). * @param rootDir The root directory of the project to contain the directory. * @param dirName The name of the directory type for logging (e.g., "logs"). * @returns The validated, absolute path to the directory, or null if invalid. */ const ensureDirectory = ( dirPath: string, rootDir: string, dirName: string, ): string | null => { const resolvedDirPath = path.isAbsolute(dirPath) ? dirPath : path.resolve(rootDir, dirPath); // Ensure the resolved path is within the project root boundary if ( !resolvedDirPath.startsWith(rootDir + path.sep) && resolvedDirPath !== rootDir ) { if (process.stdout.isTTY) { console.error( `Error: ${dirName} path "${dirPath}" resolves to "${resolvedDirPath}", which is outside the project boundary "${rootDir}".`, ); } return null; } if (!existsSync(resolvedDirPath)) { try { mkdirSync(resolvedDirPath, { recursive: true }); if (process.stdout.isTTY) { console.log(`Created ${dirName} directory: ${resolvedDirPath}`); } } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : String(err); if (process.stdout.isTTY) { console.error( `Error creating ${dirName} directory at ${resolvedDirPath}: ${errorMessage}`, ); } return null; } } else { try { const stats = statSync(resolvedDirPath); if (!stats.isDirectory()) { if (process.stdout.isTTY) { console.error( `Error: ${dirName} path ${resolvedDirPath} exists but is not a directory.`, ); } return null; } } catch (statError: any) { if (process.stdout.isTTY) { console.error( `Error accessing ${dirName} path ${resolvedDirPath}: ${statError.message}`, ); } return null; } } return resolvedDirPath; }; // --- End Directory Ensurance Function --- // --- Logs Directory Handling --- const validatedLogsPath = ensureDirectory(env.LOGS_DIR, projectRoot, "logs"); if (!validatedLogsPath) { if (process.stdout.isTTY) { console.error( "FATAL: Logs directory configuration is invalid or could not be created. Please check permissions and path. Exiting.", ); } process.exit(1); // Exit if logs directory is not usable } // --- End Logs Directory Handling --- /** * Main application configuration object. * Aggregates settings from validated environment variables and `package.json`. */ export const config = { /** MCP server name. Env `MCP_SERVER_NAME` > `package.json` name > "mcp-ts-template". */ mcpServerName: env.MCP_SERVER_NAME || pkg.name, /** MCP server version. Env `MCP_SERVER_VERSION` > `package.json` version > "0.0.0". */ mcpServerVersion: env.MCP_SERVER_VERSION || pkg.version, /** Logging level. From `MCP_LOG_LEVEL` env var. Default: "debug". */ logLevel: env.MCP_LOG_LEVEL, /** Absolute path to the logs directory. From `LOGS_DIR` env var. */ logsPath: validatedLogsPath, /** Runtime environment. From `NODE_ENV` env var. Default: "development". */ environment: env.NODE_ENV, /** MCP transport type ('stdio' or 'http'). From `MCP_TRANSPORT_TYPE` env var. Default: "stdio". */ mcpTransportType: env.MCP_TRANSPORT_TYPE, /** HTTP server port (if http transport). From `MCP_HTTP_PORT` env var. Default: 3010. */ mcpHttpPort: env.MCP_HTTP_PORT, /** HTTP server host (if http transport). From `MCP_HTTP_HOST` env var. Default: "127.0.0.1". */ mcpHttpHost: env.MCP_HTTP_HOST, /** Array of allowed CORS origins (http transport). From `MCP_ALLOWED_ORIGINS` (comma-separated). */ mcpAllowedOrigins: env.MCP_ALLOWED_ORIGINS?.split(",") .map((origin) => origin.trim()) .filter(Boolean), /** Auth secret key (JWTs, http transport). From `MCP_AUTH_SECRET_KEY`. CRITICAL. */ mcpAuthSecretKey: env.MCP_AUTH_SECRET_KEY, /** OpenRouter App URL. From `OPENROUTER_APP_URL`. Default: "http://localhost:3000". */ openrouterAppUrl: env.OPENROUTER_APP_URL || "http://localhost:3000", /** OpenRouter App Name. From `OPENROUTER_APP_NAME`. Defaults to `mcpServerName`. */ openrouterAppName: env.OPENROUTER_APP_NAME || pkg.name || "MCP TS App", /** OpenRouter API Key. From `OPENROUTER_API_KEY`. */ openrouterApiKey: env.OPENROUTER_API_KEY, /** Default LLM model. From `LLM_DEFAULT_MODEL`. */ llmDefaultModel: env.LLM_DEFAULT_MODEL, /** Default LLM temperature. From `LLM_DEFAULT_TEMPERATURE`. */ llmDefaultTemperature: env.LLM_DEFAULT_TEMPERATURE, /** Default LLM top_p. From `LLM_DEFAULT_TOP_P`. */ llmDefaultTopP: env.LLM_DEFAULT_TOP_P, /** Default LLM max tokens. From `LLM_DEFAULT_MAX_TOKENS`. */ llmDefaultMaxTokens: env.LLM_DEFAULT_MAX_TOKENS, /** Default LLM top_k. From `LLM_DEFAULT_TOP_K`. */ llmDefaultTopK: env.LLM_DEFAULT_TOP_K, /** Default LLM min_p. From `LLM_DEFAULT_MIN_P`. */ llmDefaultMinP: env.LLM_DEFAULT_MIN_P, /** Gemini API Key. From `GEMINI_API_KEY`. */ geminiApiKey: env.GEMINI_API_KEY, /** OAuth Proxy configurations. Undefined if no related env vars are set. */ oauthProxy: env.OAUTH_PROXY_AUTHORIZATION_URL || env.OAUTH_PROXY_TOKEN_URL || env.OAUTH_PROXY_REVOCATION_URL || env.OAUTH_PROXY_ISSUER_URL || env.OAUTH_PROXY_SERVICE_DOCUMENTATION_URL || env.OAUTH_PROXY_DEFAULT_CLIENT_REDIRECT_URIS ? { authorizationUrl: env.OAUTH_PROXY_AUTHORIZATION_URL, tokenUrl: env.OAUTH_PROXY_TOKEN_URL, revocationUrl: env.OAUTH_PROXY_REVOCATION_URL, issuerUrl: env.OAUTH_PROXY_ISSUER_URL, serviceDocumentationUrl: env.OAUTH_PROXY_SERVICE_DOCUMENTATION_URL, defaultClientRedirectUris: env.OAUTH_PROXY_DEFAULT_CLIENT_REDIRECT_URIS?.split(",") .map((uri) => uri.trim()) .filter(Boolean), } : undefined, /** Base directory for filesystem operations. From `FS_BASE_DIRECTORY`. If set, operations are restricted to this path. Will be an absolute path. */ fsBaseDirectory: resolvedFsBaseDirectory, }; /** * Configured logging level for the application. * Exported for convenience. */ export const logLevel: string = config.logLevel; /** * Configured runtime environment ("development", "production", etc.). * Exported for convenience. */ export const environment: string = config.environment;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cyanheads/filesystem-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server