@prmichaelsen/acp-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +5 -0
- package/AGENT.md +1279 -0
- package/README.md +78 -0
- package/agent/commands/acp.command-create.md +372 -0
- package/agent/commands/acp.design-create.md +224 -0
- package/agent/commands/acp.init.md +410 -0
- package/agent/commands/acp.package-create.md +894 -0
- package/agent/commands/acp.package-info.md +211 -0
- package/agent/commands/acp.package-install.md +461 -0
- package/agent/commands/acp.package-list.md +279 -0
- package/agent/commands/acp.package-publish.md +540 -0
- package/agent/commands/acp.package-remove.md +292 -0
- package/agent/commands/acp.package-search.md +306 -0
- package/agent/commands/acp.package-update.md +310 -0
- package/agent/commands/acp.package-validate.md +535 -0
- package/agent/commands/acp.pattern-create.md +326 -0
- package/agent/commands/acp.plan.md +552 -0
- package/agent/commands/acp.proceed.md +336 -0
- package/agent/commands/acp.project-create.md +672 -0
- package/agent/commands/acp.report.md +394 -0
- package/agent/commands/acp.resume.md +237 -0
- package/agent/commands/acp.status.md +280 -0
- package/agent/commands/acp.sync.md +363 -0
- package/agent/commands/acp.task-create.md +390 -0
- package/agent/commands/acp.update.md +301 -0
- package/agent/commands/acp.validate.md +436 -0
- package/agent/commands/acp.version-check-for-updates.md +275 -0
- package/agent/commands/acp.version-check.md +190 -0
- package/agent/commands/acp.version-update.md +288 -0
- package/agent/commands/command.template.md +316 -0
- package/agent/commands/git.commit.md +513 -0
- package/agent/commands/git.init.md +513 -0
- package/agent/commands/mcp-server-starter.add-tool.md +677 -0
- package/agent/commands/mcp-server-starter.init.md +894 -0
- package/agent/design/.gitkeep +0 -0
- package/agent/design/design.template.md +136 -0
- package/agent/design/remember-mcp-analysis.md +987 -0
- package/agent/design/requirements.template.md +387 -0
- package/agent/manifest.template.yaml +13 -0
- package/agent/manifest.yaml +109 -0
- package/agent/milestones/.gitkeep +0 -0
- package/agent/milestones/milestone-1-{title}.template.md +206 -0
- package/agent/package.template.yaml +36 -0
- package/agent/patterns/.gitkeep +0 -0
- package/agent/patterns/bootstrap.template.md +1237 -0
- package/agent/patterns/mcp-server-starter.bootstrap.md +597 -0
- package/agent/patterns/mcp-server-starter.build-config.md +554 -0
- package/agent/patterns/mcp-server-starter.config-management.md +525 -0
- package/agent/patterns/mcp-server-starter.server-factory.md +616 -0
- package/agent/patterns/mcp-server-starter.server-standalone.md +642 -0
- package/agent/patterns/mcp-server-starter.test-config.md +558 -0
- package/agent/patterns/mcp-server-starter.tool-creation.md +653 -0
- package/agent/patterns/pattern.template.md +364 -0
- package/agent/progress.template.yaml +161 -0
- package/agent/progress.yaml +33 -0
- package/agent/schemas/package.schema.yaml +161 -0
- package/agent/scripts/acp.common.sh +1362 -0
- package/agent/scripts/acp.install.sh +213 -0
- package/agent/scripts/acp.package-create.sh +925 -0
- package/agent/scripts/acp.package-info.sh +270 -0
- package/agent/scripts/acp.package-install.sh +550 -0
- package/agent/scripts/acp.package-list.sh +263 -0
- package/agent/scripts/acp.package-publish.sh +420 -0
- package/agent/scripts/acp.package-remove.sh +272 -0
- package/agent/scripts/acp.package-search.sh +156 -0
- package/agent/scripts/acp.package-update.sh +356 -0
- package/agent/scripts/acp.package-validate.sh +766 -0
- package/agent/scripts/acp.uninstall.sh +85 -0
- package/agent/scripts/acp.version-check-for-updates.sh +98 -0
- package/agent/scripts/acp.version-check.sh +47 -0
- package/agent/scripts/acp.version-update.sh +158 -0
- package/agent/scripts/acp.yaml-parser.sh +736 -0
- package/agent/scripts/acp.yaml-validate.sh +205 -0
- package/agent/tasks/.gitkeep +0 -0
- package/agent/tasks/task-1-{title}.template.md +225 -0
- package/dist/config.d.ts +4 -0
- package/dist/server-factory.d.ts +9 -0
- package/dist/server-factory.js +99 -0
- package/dist/server-factory.js.map +7 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +106 -0
- package/dist/server.js.map +7 -0
- package/dist/tools/acp-remote-list-files.d.ts +15 -0
- package/dist/types/ssh-config.d.ts +16 -0
- package/esbuild.build.js +34 -0
- package/esbuild.watch.js +31 -0
- package/jest.config.js +31 -0
- package/package.json +54 -0
- package/src/config.ts +16 -0
- package/src/server-factory.ts +43 -0
- package/src/server.ts +46 -0
- package/src/tools/acp-remote-list-files.ts +89 -0
- package/src/types/ssh-config.ts +17 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
# Config Management Pattern
|
|
2
|
+
|
|
3
|
+
**Pattern**: Environment Variable and Configuration Management
|
|
4
|
+
**Category**: Configuration
|
|
5
|
+
**Complexity**: Beginner
|
|
6
|
+
**Last Updated**: 2026-02-22
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
The Config Management Pattern provides type-safe, validated configuration management for MCP servers using environment variables, with proper defaults and error handling.
|
|
13
|
+
|
|
14
|
+
**When to use this pattern**:
|
|
15
|
+
- Managing environment-specific configuration
|
|
16
|
+
- Handling API keys and secrets
|
|
17
|
+
- Configuring database connections
|
|
18
|
+
- Setting server options
|
|
19
|
+
- Multi-environment deployments
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Core Principles
|
|
24
|
+
|
|
25
|
+
### 1. Type Safety
|
|
26
|
+
Use TypeScript for compile-time configuration validation.
|
|
27
|
+
|
|
28
|
+
### 2. Validation
|
|
29
|
+
Validate required configuration at startup.
|
|
30
|
+
|
|
31
|
+
### 3. Defaults
|
|
32
|
+
Provide sensible defaults for optional configuration.
|
|
33
|
+
|
|
34
|
+
### 4. Immutability
|
|
35
|
+
Configuration should be read-only (`as const`).
|
|
36
|
+
|
|
37
|
+
### 5. Documentation
|
|
38
|
+
Document all configuration in `.env.example`.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Complete Configuration
|
|
43
|
+
|
|
44
|
+
**src/config.ts**:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import dotenv from 'dotenv';
|
|
48
|
+
import { logger } from './utils/logger.js';
|
|
49
|
+
|
|
50
|
+
// Load environment variables
|
|
51
|
+
dotenv.config();
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Application configuration
|
|
55
|
+
*
|
|
56
|
+
* All configuration is loaded from environment variables.
|
|
57
|
+
* See .env.example for required variables.
|
|
58
|
+
*/
|
|
59
|
+
export const config = {
|
|
60
|
+
// Service configuration
|
|
61
|
+
service: {
|
|
62
|
+
url: process.env.SERVICE_URL || 'http://localhost:8080',
|
|
63
|
+
apiKey: process.env.SERVICE_API_KEY || '',
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Server configuration
|
|
67
|
+
server: {
|
|
68
|
+
port: parseInt(process.env.PORT || '3000', 10),
|
|
69
|
+
nodeEnv: process.env.NODE_ENV || 'development',
|
|
70
|
+
logLevel: process.env.LOG_LEVEL || 'info',
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// MCP configuration
|
|
74
|
+
mcp: {
|
|
75
|
+
transport: process.env.MCP_TRANSPORT || 'stdio',
|
|
76
|
+
},
|
|
77
|
+
} as const;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Validate required configuration
|
|
81
|
+
*
|
|
82
|
+
* Call this at server startup to ensure all required
|
|
83
|
+
* configuration is present.
|
|
84
|
+
*
|
|
85
|
+
* @throws Error if required configuration is missing
|
|
86
|
+
*/
|
|
87
|
+
export function validateConfig(): void {
|
|
88
|
+
const required = [
|
|
89
|
+
{ key: 'SERVICE_URL', value: config.service.url },
|
|
90
|
+
{ key: 'SERVICE_API_KEY', value: config.service.apiKey },
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
const missing = required.filter((r) => !r.value || r.value === 'http://localhost:8080');
|
|
94
|
+
|
|
95
|
+
if (missing.length > 0) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Missing required environment variables: ${missing.map((m) => m.key).join(', ')}`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
logger.info('Configuration validated', {
|
|
102
|
+
nodeEnv: config.server.nodeEnv,
|
|
103
|
+
logLevel: config.server.logLevel,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Environment Variables
|
|
111
|
+
|
|
112
|
+
**.env.example**:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Service Configuration
|
|
116
|
+
SERVICE_URL=http://localhost:8080
|
|
117
|
+
SERVICE_API_KEY=
|
|
118
|
+
|
|
119
|
+
# Server Configuration
|
|
120
|
+
PORT=3000
|
|
121
|
+
NODE_ENV=development
|
|
122
|
+
LOG_LEVEL=info
|
|
123
|
+
|
|
124
|
+
# MCP Configuration
|
|
125
|
+
MCP_TRANSPORT=stdio
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**.env** (not committed):
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Service Configuration
|
|
132
|
+
SERVICE_URL=https://api.example.com
|
|
133
|
+
SERVICE_API_KEY=sk-1234567890abcdef
|
|
134
|
+
|
|
135
|
+
# Server Configuration
|
|
136
|
+
PORT=3000
|
|
137
|
+
NODE_ENV=production
|
|
138
|
+
LOG_LEVEL=info
|
|
139
|
+
|
|
140
|
+
# MCP Configuration
|
|
141
|
+
MCP_TRANSPORT=sse
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**.gitignore**:
|
|
145
|
+
|
|
146
|
+
```gitignore
|
|
147
|
+
.env
|
|
148
|
+
.env.local
|
|
149
|
+
.env.*.local
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Usage
|
|
155
|
+
|
|
156
|
+
### In Server
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { config, validateConfig } from './config.js';
|
|
160
|
+
|
|
161
|
+
async function initServer() {
|
|
162
|
+
// Validate configuration first
|
|
163
|
+
validateConfig();
|
|
164
|
+
|
|
165
|
+
// Use configuration
|
|
166
|
+
const client = new ServiceClient({
|
|
167
|
+
url: config.service.url,
|
|
168
|
+
apiKey: config.service.apiKey,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// ...
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### In Tools
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { config } from '../config.js';
|
|
179
|
+
|
|
180
|
+
export async function handleTool(args, userId) {
|
|
181
|
+
// Access configuration
|
|
182
|
+
const response = await fetch(config.service.url, {
|
|
183
|
+
headers: {
|
|
184
|
+
'Authorization': `Bearer ${config.service.apiKey}`,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// ...
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Advanced Patterns
|
|
195
|
+
|
|
196
|
+
### Nested Configuration
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
export const config = {
|
|
200
|
+
database: {
|
|
201
|
+
host: process.env.DB_HOST || 'localhost',
|
|
202
|
+
port: parseInt(process.env.DB_PORT || '5432', 10),
|
|
203
|
+
name: process.env.DB_NAME || 'mydb',
|
|
204
|
+
user: process.env.DB_USER || 'user',
|
|
205
|
+
password: process.env.DB_PASSWORD || '',
|
|
206
|
+
ssl: process.env.DB_SSL === 'true',
|
|
207
|
+
pool: {
|
|
208
|
+
min: parseInt(process.env.DB_POOL_MIN || '2', 10),
|
|
209
|
+
max: parseInt(process.env.DB_POOL_MAX || '10', 10),
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
} as const;
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Environment-Specific Config
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
219
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
220
|
+
|
|
221
|
+
export const config = {
|
|
222
|
+
server: {
|
|
223
|
+
port: parseInt(process.env.PORT || (isDevelopment ? '3000' : '8080'), 10),
|
|
224
|
+
cors: isDevelopment,
|
|
225
|
+
debug: isDevelopment,
|
|
226
|
+
},
|
|
227
|
+
logging: {
|
|
228
|
+
level: process.env.LOG_LEVEL || (isDevelopment ? 'debug' : 'info'),
|
|
229
|
+
pretty: isDevelopment,
|
|
230
|
+
},
|
|
231
|
+
} as const;
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Type-Safe Enums
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const LOG_LEVELS = ['debug', 'info', 'warn', 'error'] as const;
|
|
238
|
+
type LogLevel = typeof LOG_LEVELS[number];
|
|
239
|
+
|
|
240
|
+
function parseLogLevel(value: string | undefined): LogLevel {
|
|
241
|
+
const level = value?.toLowerCase();
|
|
242
|
+
if (level && LOG_LEVELS.includes(level as LogLevel)) {
|
|
243
|
+
return level as LogLevel;
|
|
244
|
+
}
|
|
245
|
+
return 'info';
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export const config = {
|
|
249
|
+
logging: {
|
|
250
|
+
level: parseLogLevel(process.env.LOG_LEVEL),
|
|
251
|
+
},
|
|
252
|
+
} as const;
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Validation with Zod
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import { z } from 'zod';
|
|
259
|
+
|
|
260
|
+
const configSchema = z.object({
|
|
261
|
+
service: z.object({
|
|
262
|
+
url: z.string().url(),
|
|
263
|
+
apiKey: z.string().min(1),
|
|
264
|
+
}),
|
|
265
|
+
server: z.object({
|
|
266
|
+
port: z.number().int().min(1).max(65535),
|
|
267
|
+
nodeEnv: z.enum(['development', 'production', 'test']),
|
|
268
|
+
}),
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
export const config = configSchema.parse({
|
|
272
|
+
service: {
|
|
273
|
+
url: process.env.SERVICE_URL,
|
|
274
|
+
apiKey: process.env.SERVICE_API_KEY,
|
|
275
|
+
},
|
|
276
|
+
server: {
|
|
277
|
+
port: parseInt(process.env.PORT || '3000', 10),
|
|
278
|
+
nodeEnv: process.env.NODE_ENV || 'development',
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Benefits
|
|
286
|
+
|
|
287
|
+
### 1. Type Safety
|
|
288
|
+
- Compile-time checking
|
|
289
|
+
- IDE autocomplete
|
|
290
|
+
- Refactoring support
|
|
291
|
+
|
|
292
|
+
### 2. Validation
|
|
293
|
+
- Catch configuration errors early
|
|
294
|
+
- Clear error messages
|
|
295
|
+
- Startup-time validation
|
|
296
|
+
|
|
297
|
+
### 3. Documentation
|
|
298
|
+
- `.env.example` documents all variables
|
|
299
|
+
- Self-documenting code
|
|
300
|
+
- Easy onboarding
|
|
301
|
+
|
|
302
|
+
### 4. Security
|
|
303
|
+
- Secrets not in code
|
|
304
|
+
- Environment-specific values
|
|
305
|
+
- `.env` not committed
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Anti-Patterns
|
|
310
|
+
|
|
311
|
+
### ❌ Don't: Hardcode Configuration
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// ❌ Wrong
|
|
315
|
+
const apiKey = 'sk-1234567890';
|
|
316
|
+
const dbUrl = 'mongodb://localhost:27017';
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
// ✅ Correct
|
|
321
|
+
const apiKey = process.env.API_KEY;
|
|
322
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Why**: Hardcoded values can't change per environment and expose secrets.
|
|
326
|
+
|
|
327
|
+
### ❌ Don't: Skip Validation
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
// ❌ Wrong
|
|
331
|
+
export const config = {
|
|
332
|
+
apiKey: process.env.API_KEY, // Might be undefined!
|
|
333
|
+
};
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
// ✅ Correct
|
|
338
|
+
export const config = {
|
|
339
|
+
apiKey: process.env.API_KEY || '',
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export function validateConfig() {
|
|
343
|
+
if (!config.apiKey) {
|
|
344
|
+
throw new Error('API_KEY is required');
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Why**: Missing configuration causes runtime errors.
|
|
350
|
+
|
|
351
|
+
### ❌ Don't: Use Mutable Configuration
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
// ❌ Wrong
|
|
355
|
+
export let config = {
|
|
356
|
+
port: 3000,
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
config.port = 8080; // Mutable!
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
// ✅ Correct
|
|
364
|
+
export const config = {
|
|
365
|
+
port: 3000,
|
|
366
|
+
} as const; // Immutable
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**Why**: Mutable configuration leads to bugs.
|
|
370
|
+
|
|
371
|
+
### ❌ Don't: Commit .env Files
|
|
372
|
+
|
|
373
|
+
```gitignore
|
|
374
|
+
# ❌ Wrong - .env not ignored
|
|
375
|
+
# (no .env entry)
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
```gitignore
|
|
379
|
+
# ✅ Correct
|
|
380
|
+
.env
|
|
381
|
+
.env.local
|
|
382
|
+
.env.*.local
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Why**: `.env` contains secrets and should never be committed.
|
|
386
|
+
|
|
387
|
+
### ❌ Don't: Use process.env Directly
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
// ❌ Wrong - Scattered throughout code
|
|
391
|
+
const url = process.env.SERVICE_URL;
|
|
392
|
+
const key = process.env.API_KEY;
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
// ✅ Correct - Centralized in config
|
|
397
|
+
import { config } from './config.js';
|
|
398
|
+
const url = config.service.url;
|
|
399
|
+
const key = config.service.apiKey;
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
**Why**: Centralized configuration is easier to manage and test.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Testing
|
|
407
|
+
|
|
408
|
+
### Mock Configuration
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
import { describe, it, expect, jest } from '@jest/globals';
|
|
412
|
+
|
|
413
|
+
// Mock config module
|
|
414
|
+
jest.mock('./config.js', () => ({
|
|
415
|
+
config: {
|
|
416
|
+
service: {
|
|
417
|
+
url: 'http://test.example.com',
|
|
418
|
+
apiKey: 'test-key',
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
}));
|
|
422
|
+
|
|
423
|
+
import { config } from './config.js';
|
|
424
|
+
import { handleTool } from './tool.js';
|
|
425
|
+
|
|
426
|
+
describe('tool with mocked config', () => {
|
|
427
|
+
it('should use test configuration', async () => {
|
|
428
|
+
expect(config.service.url).toBe('http://test.example.com');
|
|
429
|
+
// Test tool with mocked config
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Test Validation
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
describe('validateConfig', () => {
|
|
438
|
+
const originalEnv = process.env;
|
|
439
|
+
|
|
440
|
+
beforeEach(() => {
|
|
441
|
+
process.env = { ...originalEnv };
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
afterEach(() => {
|
|
445
|
+
process.env = originalEnv;
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('should pass with valid config', () => {
|
|
449
|
+
process.env.SERVICE_URL = 'https://api.example.com';
|
|
450
|
+
process.env.SERVICE_API_KEY = 'sk-123';
|
|
451
|
+
|
|
452
|
+
expect(() => validateConfig()).not.toThrow();
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('should fail with missing API key', () => {
|
|
456
|
+
process.env.SERVICE_URL = 'https://api.example.com';
|
|
457
|
+
delete process.env.SERVICE_API_KEY;
|
|
458
|
+
|
|
459
|
+
expect(() => validateConfig()).toThrow('SERVICE_API_KEY');
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## Related Patterns
|
|
467
|
+
|
|
468
|
+
- [Bootstrap Pattern](mcp-server-starter.bootstrap.md) - Initial setup
|
|
469
|
+
- [Server Standalone Pattern](mcp-server-starter.server-standalone.md) - Using config
|
|
470
|
+
- [Server Factory Pattern](mcp-server-starter.server-factory.md) - Multi-tenant config
|
|
471
|
+
- [Tool Creation Pattern](mcp-server-starter.tool-creation.md) - Config in tools
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Troubleshooting
|
|
476
|
+
|
|
477
|
+
**Problem**: Environment variables not loaded
|
|
478
|
+
|
|
479
|
+
**Solution**: Ensure `dotenv.config()` is called early:
|
|
480
|
+
```typescript
|
|
481
|
+
import dotenv from 'dotenv';
|
|
482
|
+
dotenv.config(); // Call first!
|
|
483
|
+
|
|
484
|
+
import { config } from './config.js';
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
**Problem**: Type errors with `as const`
|
|
488
|
+
|
|
489
|
+
**Solution**: Use type assertions for dynamic values:
|
|
490
|
+
```typescript
|
|
491
|
+
export const config = {
|
|
492
|
+
port: parseInt(process.env.PORT || '3000', 10) as number,
|
|
493
|
+
} as const;
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Problem**: Validation fails in tests
|
|
497
|
+
|
|
498
|
+
**Solution**: Mock environment variables in tests:
|
|
499
|
+
```typescript
|
|
500
|
+
beforeEach(() => {
|
|
501
|
+
process.env.API_KEY = 'test-key';
|
|
502
|
+
});
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## Checklist
|
|
508
|
+
|
|
509
|
+
- [ ] Created `src/config.ts`
|
|
510
|
+
- [ ] Loaded environment variables with dotenv
|
|
511
|
+
- [ ] Defined configuration object with types
|
|
512
|
+
- [ ] Made configuration immutable (`as const`)
|
|
513
|
+
- [ ] Created validation function
|
|
514
|
+
- [ ] Provided sensible defaults
|
|
515
|
+
- [ ] Created `.env.example` template
|
|
516
|
+
- [ ] Added `.env` to `.gitignore`
|
|
517
|
+
- [ ] Called `validateConfig()` at startup
|
|
518
|
+
- [ ] Documented all environment variables
|
|
519
|
+
- [ ] Tested configuration validation
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
**Pattern**: Config Management
|
|
524
|
+
**Status**: Production Ready
|
|
525
|
+
**Last Updated**: 2026-02-22
|