--- name: adding-env-variables description: "Guide for adding new environment variables to the codebase. Ensures env.ts schemas include descriptions and .env.example is kept in sync. Triggers on: add env variable, new environment variable, env.ts change, add config variable, INKEEP_, adding to .env." --- # Adding Environment Variables Guide Comprehensive guidance for adding new environment variables to the Inkeep Agent Framework. This ensures consistency, documentation, and type safety across all packages. --- ## CI Enforcement **Environment variable descriptions are enforced by CI.** The `check:env-descriptions` script runs in CI and will fail if any variables in `env.ts` files are missing `.describe()` calls. Run locally to check: ```bash pnpm check:env-descriptions ``` --- ## Environment Architecture The framework uses environment variables defined in multiple `env.ts` files: | Package | File | Purpose | |---------|------|---------| | **agents-api** | `agents-api/src/env.ts` | Main API server configuration | | **agents-core** | `packages/agents-core/src/env.ts` | Shared core configuration | | **agents-cli** | `agents-cli/src/env.ts` | CLI tool configuration | **Note:** The following files are auto-generated and should NOT be edited: - `packages/agents-mcp/src/lib/env.ts` (Generated by Speakeasy) --- ## Required Steps for Adding Environment Variables ### Step 1: Add to `.env.example` Location: `.env.example` (root of repository) Add the variable with a descriptive comment: ```bash # ============ SECTION NAME ============ # Description of what this variable does # Additional context if needed (e.g., where to get API keys) MY_NEW_VARIABLE=default-value-or-empty ``` **Example:** ```bash # ============ AI PROVIDERS ============ # Required for agent execution # Get your API keys from: # Anthropic: https://console.anthropic.com/ # OpenAI: https://platform.openai.com/ ANTHROPIC_API_KEY= OPENAI_API_KEY= ``` ### Step 2: Add to Relevant `env.ts` File(s) Add the variable to the Zod schema with a `.describe()` call that matches the `.env.example` comment: ```typescript const envSchema = z.object({ // ... existing variables ... MY_NEW_VARIABLE: z .string() .optional() .describe('Description of what this variable does'), }); ``` ### Step 3: Description Requirements **Every environment variable MUST have a `.describe()` call** with a clear, concise description that: 1. Explains what the variable is used for 2. Matches the comment in `.env.example` 3. Includes helpful context (e.g., where to get API keys, default behavior) **Examples of good descriptions:** ```typescript // AI Provider keys ANTHROPIC_API_KEY: z .string() .describe('Anthropic API key for Claude models (required for agent execution). Get from https://console.anthropic.com/'), // Database configuration INKEEP_AGENTS_MANAGE_DATABASE_URL: z .string() .describe('PostgreSQL connection URL for the management database (Doltgres with Git version control)'), // Authentication BETTER_AUTH_SECRET: z .string() .optional() .describe('Secret key for Better Auth session encryption (change in production)'), // Feature flags LANGFUSE_ENABLED: z .string() .optional() .transform((val) => val === 'true') .describe('Enable Langfuse LLM observability (set to "true" to enable)'), ``` --- ## Zod Schema Patterns ### Required Variables ```typescript MY_REQUIRED_VAR: z .string() .describe('This variable is required for the application to function'), ``` ### Optional Variables ```typescript MY_OPTIONAL_VAR: z .string() .optional() .describe('Optional configuration for feature X'), ``` ### Variables with Defaults ```typescript MY_VAR_WITH_DEFAULT: z .string() .optional() .default('default-value') .describe('Configuration with a sensible default'), ``` ### Enum Variables ```typescript LOG_LEVEL: z .enum(['trace', 'debug', 'info', 'warn', 'error']) .default('info') .describe('Logging verbosity level'), ``` ### Numeric Variables ```typescript POOL_SIZE: z .coerce.number() .optional() .default(10) .describe('Maximum number of connections in the pool'), ``` ### Boolean Variables (as strings) ```typescript FEATURE_ENABLED: z .string() .optional() .transform((val) => val === 'true') .describe('Enable feature X (set to "true" to enable)'), ``` ### Variables with Validation ```typescript JWT_SECRET: z .string() .min(32, 'JWT_SECRET must be at least 32 characters') .optional() .describe('Secret key for signing JWT tokens (minimum 32 characters)'), ADMIN_EMAIL: z .string() .optional() .refine((val) => !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val), { message: 'Invalid email address', }) .describe('Admin email address for notifications'), ``` --- ## Category Organization Group related variables with comments in both `.env.example` and `env.ts`: ### In `.env.example`: ```bash # ============ DATABASE ============ INKEEP_AGENTS_MANAGE_DATABASE_URL=... INKEEP_AGENTS_RUN_DATABASE_URL=... # ============ AI PROVIDERS ============ ANTHROPIC_API_KEY= OPENAI_API_KEY= ``` ### In `env.ts`: ```typescript const envSchema = z.object({ // Database INKEEP_AGENTS_MANAGE_DATABASE_URL: z .string() .describe('PostgreSQL connection URL for the management database'), INKEEP_AGENTS_RUN_DATABASE_URL: z .string() .describe('PostgreSQL connection URL for the runtime database'), // AI Providers ANTHROPIC_API_KEY: z .string() .describe('Anthropic API key for Claude models'), OPENAI_API_KEY: z .string() .optional() .describe('OpenAI API key for GPT models'), }); ``` --- ## Which `env.ts` File to Use? Choose the appropriate file based on where the variable is used: | Use Case | File | |----------|------| | API server configuration | `agents-api/src/env.ts` | | Shared across packages | `packages/agents-core/src/env.ts` | | CLI-specific | `agents-cli/src/env.ts` | | Multiple packages | Add to `agents-core` and import where needed | --- ## Checklist for Adding Environment Variables Before completing any environment variable addition, verify: - [ ] Variable added to `.env.example` with descriptive comment - [ ] Variable added to relevant `env.ts` file(s) - [ ] `.describe()` call added with clear description - [ ] Description matches `.env.example` comment - [ ] Appropriate Zod type used (string, number, enum, etc.) - [ ] `optional()` added if variable is not required - [ ] `default()` added if there's a sensible default - [ ] Validation added if needed (min, max, refine, etc.) - [ ] Variable grouped with related variables (using comments) - [ ] `pnpm check:env-descriptions` passes locally --- ## Common Mistakes to Avoid 1. **Missing `.describe()` call** - Every variable needs a description (CI will fail!) 2. **Inconsistent descriptions** - Keep `.env.example` and `.describe()` in sync 3. **Wrong file** - Add to the package that actually uses the variable 4. **Missing validation** - Add constraints for sensitive values (min length for secrets, email validation, etc.) 5. **Editing auto-generated files** - Never edit `agents-mcp` env files