@kya-os/create-mcpi-app 1.7.38-canary.2 → 1.7.38

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.
Files changed (67) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test$colon$coverage.log +755 -0
  3. package/.turbo/turbo-test.log +200 -0
  4. package/dist/helpers/fetch-cloudflare-mcpi-template.d.ts.map +1 -1
  5. package/dist/helpers/fetch-cloudflare-mcpi-template.js +35 -914
  6. package/dist/helpers/fetch-cloudflare-mcpi-template.js.map +1 -1
  7. package/dist/utils/fetch-remote-config.d.ts.map +1 -1
  8. package/dist/utils/fetch-remote-config.js +2 -2
  9. package/dist/utils/fetch-remote-config.js.map +1 -1
  10. package/package/package.json +77 -0
  11. package/package.json +1 -1
  12. package/ARCHITECTURE_ANALYSIS.md +0 -392
  13. package/CHANGELOG.md +0 -372
  14. package/DEPRECATION_WARNINGS_ANALYSIS.md +0 -192
  15. package/IMPLEMENTATION_SUMMARY.md +0 -108
  16. package/REMEDIATION_PLAN.md +0 -99
  17. package/dist/.tsbuildinfo +0 -1
  18. package/scripts/prepare-pack.js +0 -47
  19. package/scripts/validate-no-workspace.js +0 -79
  20. package/src/__tests__/cloudflare-template.test.ts +0 -490
  21. package/src/__tests__/helpers/fetch-cloudflare-mcpi-template.test.ts +0 -337
  22. package/src/__tests__/helpers/generate-config.test.ts +0 -312
  23. package/src/__tests__/helpers/generate-identity.test.ts +0 -271
  24. package/src/__tests__/helpers/install.test.ts +0 -370
  25. package/src/__tests__/helpers/validate-project-structure.test.ts +0 -467
  26. package/src/__tests__.bak/regression.test.ts +0 -434
  27. package/src/effects/index.ts +0 -80
  28. package/src/helpers/__tests__/config-builder.spec.ts +0 -231
  29. package/src/helpers/apply-identity-preset.ts +0 -209
  30. package/src/helpers/config-builder.ts +0 -165
  31. package/src/helpers/copy-template.ts +0 -11
  32. package/src/helpers/create.ts +0 -239
  33. package/src/helpers/fetch-cloudflare-mcpi-template.ts +0 -2404
  34. package/src/helpers/fetch-cloudflare-template.ts +0 -361
  35. package/src/helpers/fetch-mcpi-template.ts +0 -236
  36. package/src/helpers/fetch-xmcp-template.ts +0 -153
  37. package/src/helpers/generate-config.ts +0 -118
  38. package/src/helpers/generate-identity.ts +0 -163
  39. package/src/helpers/identity-manager.ts +0 -186
  40. package/src/helpers/install.ts +0 -79
  41. package/src/helpers/rename.ts +0 -17
  42. package/src/helpers/validate-project-structure.ts +0 -127
  43. package/src/index.ts +0 -520
  44. package/src/utils/__tests__/fetch-remote-config.test.ts +0 -271
  45. package/src/utils/check-node.ts +0 -17
  46. package/src/utils/fetch-remote-config.ts +0 -179
  47. package/src/utils/is-folder-empty.ts +0 -60
  48. package/src/utils/validate-project-name.ts +0 -132
  49. package/test-cloudflare/README.md +0 -164
  50. package/test-cloudflare/package.json +0 -28
  51. package/test-cloudflare/src/index.ts +0 -341
  52. package/test-cloudflare/src/tools/greet.ts +0 -19
  53. package/test-cloudflare/tests/cache-invalidation.test.ts +0 -410
  54. package/test-cloudflare/tests/cors-security.test.ts +0 -349
  55. package/test-cloudflare/tests/delegation.test.ts +0 -335
  56. package/test-cloudflare/tests/do-routing.test.ts +0 -314
  57. package/test-cloudflare/tests/integration.test.ts +0 -205
  58. package/test-cloudflare/tests/session-management.test.ts +0 -359
  59. package/test-cloudflare/tsconfig.json +0 -16
  60. package/test-cloudflare/vitest.config.ts +0 -9
  61. package/test-cloudflare/wrangler.toml +0 -37
  62. package/test-node/README.md +0 -44
  63. package/test-node/package.json +0 -23
  64. package/test-node/src/tools/greet.ts +0 -25
  65. package/test-node/xmcp.config.ts +0 -20
  66. package/tsconfig.json +0 -26
  67. package/vitest.config.ts +0 -14
@@ -1,337 +0,0 @@
1
- /**
2
- * Fetch Cloudflare MCP-I Template Tests
3
- *
4
- * Tests for template fetching logic, caching, network failure handling,
5
- * and template transformation.
6
- */
7
-
8
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
9
- import { fetchCloudflareMcpiTemplate } from '../../helpers/fetch-cloudflare-mcpi-template';
10
- import fs from 'fs-extra';
11
- import path from 'path';
12
-
13
- describe('fetchCloudflareMcpiTemplate', () => {
14
- let tempDir: string;
15
- const originalConsoleLog = console.log;
16
- const originalConsoleError = console.error;
17
-
18
- beforeEach(() => {
19
- tempDir = path.join(process.cwd(), 'test-temp', `fetch-test-${Date.now()}-${Math.random().toString(36).substring(7)}`);
20
- fs.ensureDirSync(tempDir);
21
- console.log = vi.fn();
22
- console.error = vi.fn();
23
- });
24
-
25
- afterEach(() => {
26
- if (fs.existsSync(tempDir)) {
27
- fs.removeSync(tempDir);
28
- }
29
- console.log = originalConsoleLog;
30
- console.error = originalConsoleError;
31
- });
32
-
33
- describe('Template fetching logic', () => {
34
- it('should fetch and generate template successfully', async () => {
35
- await fetchCloudflareMcpiTemplate(tempDir, {
36
- packageManager: 'npm',
37
- projectName: 'test-project'
38
- });
39
-
40
- // Verify template was generated
41
- expect(fs.existsSync(path.join(tempDir, 'package.json'))).toBe(true);
42
- expect(fs.existsSync(path.join(tempDir, 'src', 'index.ts'))).toBe(true);
43
- });
44
-
45
- it('should use default project name from path when not provided', async () => {
46
- const customPath = path.join(tempDir, 'my-project');
47
- await fetchCloudflareMcpiTemplate(customPath, {
48
- packageManager: 'npm'
49
- });
50
-
51
- const packageJsonPath = path.join(customPath, 'package.json');
52
- const packageJson = fs.readJsonSync(packageJsonPath);
53
- expect(packageJson.name).toBe('my-project');
54
- });
55
-
56
- it('should handle different package managers', async () => {
57
- for (const pm of ['npm', 'yarn', 'pnpm']) {
58
- const pmDir = path.join(tempDir, `test-${pm}`);
59
- await fetchCloudflareMcpiTemplate(pmDir, {
60
- packageManager: pm,
61
- projectName: `test-${pm}`
62
- });
63
-
64
- const packageJsonPath = path.join(pmDir, 'package.json');
65
- expect(fs.existsSync(packageJsonPath)).toBe(true);
66
- }
67
- });
68
-
69
- it('should generate template with correct project structure', async () => {
70
- await fetchCloudflareMcpiTemplate(tempDir, {
71
- packageManager: 'npm',
72
- projectName: 'test-project'
73
- });
74
-
75
- // Check directory structure
76
- expect(fs.existsSync(path.join(tempDir, 'src'))).toBe(true);
77
- expect(fs.existsSync(path.join(tempDir, 'src', 'tools'))).toBe(true);
78
- expect(fs.existsSync(path.join(tempDir, 'scripts'))).toBe(true);
79
- expect(fs.existsSync(path.join(tempDir, 'tests'))).toBe(true);
80
- });
81
- });
82
-
83
- describe('Template caching', () => {
84
- it('should generate consistent template structure on multiple calls', async () => {
85
- const dir1 = path.join(tempDir, 'project1');
86
- const dir2 = path.join(tempDir, 'project2');
87
-
88
- await fetchCloudflareMcpiTemplate(dir1, {
89
- packageManager: 'npm',
90
- projectName: 'project1'
91
- });
92
-
93
- await fetchCloudflareMcpiTemplate(dir2, {
94
- packageManager: 'npm',
95
- projectName: 'project2'
96
- });
97
-
98
- // Both should have same structure
99
- const files1 = fs.readdirSync(dir1);
100
- const files2 = fs.readdirSync(dir2);
101
-
102
- // Should have same top-level files
103
- expect(files1.sort()).toEqual(files2.sort());
104
- });
105
-
106
- it('should handle template generation without overwriting existing files incorrectly', async () => {
107
- // Create existing file
108
- const existingFile = path.join(tempDir, 'package.json');
109
- fs.writeJsonSync(existingFile, { name: 'existing' });
110
-
111
- // Generate template - should overwrite
112
- await fetchCloudflareMcpiTemplate(tempDir, {
113
- packageManager: 'npm',
114
- projectName: 'new-project'
115
- });
116
-
117
- const packageJson = fs.readJsonSync(existingFile);
118
- expect(packageJson.name).toBe('new-project');
119
- });
120
- });
121
-
122
- describe('Error handling for network failures', () => {
123
- it('should handle missing dependencies gracefully', async () => {
124
- // This test verifies that the function doesn't fail if dependencies are missing
125
- // Since this is a local file operation, we test that it handles file system errors
126
- const invalidPath = '/invalid/path/that/does/not/exist';
127
-
128
- await expect(async () => {
129
- await fetchCloudflareMcpiTemplate(invalidPath, {
130
- packageManager: 'npm',
131
- projectName: 'test'
132
- });
133
- }).rejects.toThrow();
134
- });
135
-
136
- it('should handle permission errors gracefully', async () => {
137
- const readOnlyDir = path.join(tempDir, 'readonly');
138
- fs.ensureDirSync(readOnlyDir);
139
-
140
- try {
141
- // Make directory read-only (if possible on this system)
142
- fs.chmodSync(readOnlyDir, 0o444);
143
-
144
- await expect(async () => {
145
- await fetchCloudflareMcpiTemplate(readOnlyDir, {
146
- packageManager: 'npm',
147
- projectName: 'test'
148
- });
149
- }).rejects.toThrow();
150
- } catch (error) {
151
- // Permission errors are expected
152
- expect(error).toBeDefined();
153
- } finally {
154
- // Restore permissions for cleanup
155
- try {
156
- fs.chmodSync(readOnlyDir, 0o755);
157
- } catch {
158
- // Ignore cleanup errors
159
- }
160
- }
161
- });
162
-
163
- it('should handle disk space errors (simulated)', async () => {
164
- // Test that function handles file write errors
165
- const diskFullDir = path.join(tempDir, 'disk-full');
166
-
167
- // Mock fs.writeFileSync to throw error
168
- const originalWriteFileSync = fs.writeFileSync;
169
- const writeFileSyncSpy = vi.spyOn(fs, 'writeFileSync').mockImplementationOnce(() => {
170
- throw new Error('ENOSPC: No space left on device');
171
- });
172
-
173
- await expect(async () => {
174
- await fetchCloudflareMcpiTemplate(diskFullDir, {
175
- packageManager: 'npm',
176
- projectName: 'test'
177
- });
178
- }).rejects.toThrow();
179
-
180
- writeFileSyncSpy.mockRestore();
181
- });
182
- });
183
-
184
- describe('Template transformation', () => {
185
- it('should transform project name to valid class name', async () => {
186
- await fetchCloudflareMcpiTemplate(tempDir, {
187
- packageManager: 'npm',
188
- projectName: 'my-awesome-project'
189
- });
190
-
191
- const indexPath = path.join(tempDir, 'src', 'index.ts');
192
- const indexContent = fs.readFileSync(indexPath, 'utf-8');
193
-
194
- // Should sanitize special characters - "my-awesome-project" becomes "MyawesomeprojectMCP"
195
- // Class name should not contain hyphens
196
- expect(indexContent).toMatch(/class\s+\w+MCP/);
197
- // Should not contain the original hyphenated name in the class declaration
198
- const classMatch = indexContent.match(/class\s+(\w+)MCP/);
199
- expect(classMatch).toBeTruthy();
200
- if (classMatch) {
201
- expect(classMatch[1]).not.toContain('-');
202
- }
203
- });
204
-
205
- it('should handle special characters in project name', async () => {
206
- await fetchCloudflareMcpiTemplate(tempDir, {
207
- packageManager: 'npm',
208
- projectName: 'test@123!project'
209
- });
210
-
211
- const indexPath = path.join(tempDir, 'src', 'index.ts');
212
- const indexContent = fs.readFileSync(indexPath, 'utf-8');
213
-
214
- // Should sanitize special characters
215
- expect(indexContent).toMatch(/class\s+\w+MCP/);
216
- });
217
-
218
- it('should handle numeric project names', async () => {
219
- await fetchCloudflareMcpiTemplate(tempDir, {
220
- packageManager: 'npm',
221
- projectName: '123project'
222
- });
223
-
224
- const indexPath = path.join(tempDir, 'src', 'index.ts');
225
- const indexContent = fs.readFileSync(indexPath, 'utf-8');
226
-
227
- // Should prefix with underscore if starts with number
228
- expect(indexContent).toMatch(/class\s+_?\w+MCP/);
229
- });
230
-
231
- it('should transform KV namespace bindings correctly', async () => {
232
- await fetchCloudflareMcpiTemplate(tempDir, {
233
- packageManager: 'npm',
234
- projectName: 'TestProject'
235
- });
236
-
237
- const wranglerPath = path.join(tempDir, 'wrangler.toml');
238
- const wranglerContent = fs.readFileSync(wranglerPath, 'utf-8');
239
-
240
- // Should use uppercase class name for KV bindings
241
- expect(wranglerContent).toContain('TESTPROJECT_');
242
- });
243
-
244
- it('should transform package.json scripts correctly', async () => {
245
- await fetchCloudflareMcpiTemplate(tempDir, {
246
- packageManager: 'npm',
247
- projectName: 'MyProject'
248
- });
249
-
250
- const packageJsonPath = path.join(tempDir, 'package.json');
251
- const packageJson = fs.readJsonSync(packageJsonPath);
252
-
253
- // Scripts should use transformed class name
254
- expect(packageJson.scripts['kv:create-nonce']).toContain('MYPROJECT_');
255
- });
256
-
257
- it('should transform index.ts class name correctly', async () => {
258
- await fetchCloudflareMcpiTemplate(tempDir, {
259
- packageManager: 'npm',
260
- projectName: 'test-project'
261
- });
262
-
263
- const indexPath = path.join(tempDir, 'src', 'index.ts');
264
- const indexContent = fs.readFileSync(indexPath, 'utf-8');
265
-
266
- // Should create PascalCase class - "test-project" becomes "TestprojectMCP"
267
- expect(indexContent).toContain('class TestprojectMCP');
268
- });
269
- });
270
-
271
- describe('API key handling', () => {
272
- it('should include API key in .dev.vars when provided', async () => {
273
- await fetchCloudflareMcpiTemplate(tempDir, {
274
- packageManager: 'npm',
275
- projectName: 'test-project',
276
- apikey: 'sk_test_1234567890'
277
- });
278
-
279
- const devVarsPath = path.join(tempDir, '.dev.vars');
280
- if (fs.existsSync(devVarsPath)) {
281
- const devVarsContent = fs.readFileSync(devVarsPath, 'utf-8');
282
- expect(devVarsContent).toContain('sk_test_1234567890');
283
- }
284
- });
285
-
286
- it('should handle missing API key gracefully', async () => {
287
- await fetchCloudflareMcpiTemplate(tempDir, {
288
- packageManager: 'npm',
289
- projectName: 'test-project'
290
- });
291
-
292
- // Template should still be generated
293
- expect(fs.existsSync(path.join(tempDir, 'package.json'))).toBe(true);
294
- });
295
- });
296
-
297
- describe('Skip identity option', () => {
298
- it('should skip identity generation when skipIdentity is true', async () => {
299
- const generateIdentitySpy = vi.spyOn(await import('../../helpers/generate-identity'), 'generateIdentity');
300
-
301
- await fetchCloudflareMcpiTemplate(tempDir, {
302
- packageManager: 'npm',
303
- projectName: 'test-project',
304
- skipIdentity: true
305
- });
306
-
307
- // generateIdentity should not be called
308
- expect(generateIdentitySpy).not.toHaveBeenCalled();
309
-
310
- generateIdentitySpy.mockRestore();
311
- });
312
-
313
- it('should generate identity when skipIdentity is false', async () => {
314
- const generateIdentitySpy = vi.spyOn(await import('../../helpers/generate-identity'), 'generateIdentity');
315
- generateIdentitySpy.mockResolvedValue({
316
- did: 'did:key:ztest',
317
- kid: 'did:key:ztest#key-1',
318
- privateKey: 'test-private',
319
- publicKey: 'test-public',
320
- createdAt: new Date().toISOString(),
321
- type: 'development'
322
- });
323
-
324
- await fetchCloudflareMcpiTemplate(tempDir, {
325
- packageManager: 'npm',
326
- projectName: 'test-project',
327
- skipIdentity: false
328
- });
329
-
330
- // generateIdentity should be called
331
- expect(generateIdentitySpy).toHaveBeenCalled();
332
-
333
- generateIdentitySpy.mockRestore();
334
- });
335
- });
336
- });
337
-
@@ -1,312 +0,0 @@
1
- /**
2
- * Generate Config Tests
3
- *
4
- * Tests for config generation from prompts, default values, config merging,
5
- * and validation rules.
6
- */
7
-
8
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
9
- import { generateConfig } from '../../helpers/generate-config';
10
- import fs from 'fs-extra';
11
- import path from 'path';
12
-
13
- describe('generateConfig', () => {
14
- let tempDir: string;
15
-
16
- beforeEach(() => {
17
- // Ensure parent directory exists first to avoid race conditions
18
- const testTempDir = path.join(process.cwd(), 'test-temp');
19
- fs.ensureDirSync(testTempDir);
20
- tempDir = path.join(testTempDir, `config-test-${Date.now()}-${Math.random().toString(36).substring(7)}`);
21
- fs.ensureDirSync(tempDir);
22
- });
23
-
24
- afterEach(() => {
25
- if (fs.existsSync(tempDir)) {
26
- fs.removeSync(tempDir);
27
- }
28
- if (fs.existsSync(path.join(process.cwd(), 'test-temp'))) {
29
- const testTempContents = fs.readdirSync(path.join(process.cwd(), 'test-temp'));
30
- if (testTempContents.length === 0) {
31
- fs.removeSync(path.join(process.cwd(), 'test-temp'));
32
- }
33
- }
34
- });
35
-
36
- describe('Config generation from prompts', () => {
37
- it('should generate xmcp.config.ts with default values', () => {
38
- generateConfig(tempDir, ['stdio'], false);
39
-
40
- const configPath = path.join(tempDir, 'xmcp.config.ts');
41
- expect(fs.existsSync(configPath)).toBe(true);
42
-
43
- const configContent = fs.readFileSync(configPath, 'utf-8');
44
- expect(configContent).toContain('XmcpConfig');
45
- expect(configContent).toContain('paths');
46
- expect(configContent).toContain('./src/tools');
47
- });
48
-
49
- it('should generate config with HTTP transport when specified', () => {
50
- generateConfig(tempDir, ['http'], false);
51
-
52
- const configPath = path.join(tempDir, 'xmcp.config.ts');
53
- const configContent = fs.readFileSync(configPath, 'utf-8');
54
-
55
- expect(configContent).toContain('http: true');
56
- expect(configContent).not.toContain('stdio: true');
57
- });
58
-
59
- it('should generate config with STDIO transport when specified', () => {
60
- generateConfig(tempDir, ['stdio'], false);
61
-
62
- const configPath = path.join(tempDir, 'xmcp.config.ts');
63
- const configContent = fs.readFileSync(configPath, 'utf-8');
64
-
65
- expect(configContent).toContain('stdio: true');
66
- expect(configContent).not.toContain('http: true');
67
- });
68
-
69
- it('should generate config with both transports when both specified', () => {
70
- generateConfig(tempDir, ['http', 'stdio'], false);
71
-
72
- const configPath = path.join(tempDir, 'xmcp.config.ts');
73
- const configContent = fs.readFileSync(configPath, 'utf-8');
74
-
75
- expect(configContent).toContain('http: true');
76
- expect(configContent).toContain('stdio: true');
77
- });
78
-
79
- it('should generate config without identity when skipIdentity is true', () => {
80
- generateConfig(tempDir, ['stdio'], true);
81
-
82
- const configPath = path.join(tempDir, 'xmcp.config.ts');
83
- const configContent = fs.readFileSync(configPath, 'utf-8');
84
-
85
- expect(configContent).not.toContain('identity:');
86
- });
87
-
88
- it('should generate config with identity when skipIdentity is false', () => {
89
- generateConfig(tempDir, ['stdio'], false);
90
-
91
- const configPath = path.join(tempDir, 'xmcp.config.ts');
92
- const configContent = fs.readFileSync(configPath, 'utf-8');
93
-
94
- expect(configContent).toContain('identity:');
95
- expect(configContent).toContain('enabled: true');
96
- });
97
- });
98
-
99
- describe('Default values', () => {
100
- it('should use default tools path', () => {
101
- generateConfig(tempDir, ['stdio'], false);
102
-
103
- const configPath = path.join(tempDir, 'xmcp.config.ts');
104
- const configContent = fs.readFileSync(configPath, 'utf-8');
105
-
106
- expect(configContent).toContain('tools: "./src/tools"');
107
- });
108
-
109
- it('should use default development environment for identity', () => {
110
- generateConfig(tempDir, ['stdio'], false);
111
-
112
- const configPath = path.join(tempDir, 'xmcp.config.ts');
113
- const configContent = fs.readFileSync(configPath, 'utf-8');
114
-
115
- expect(configContent).toContain('environment: "development"');
116
- });
117
-
118
- it('should generate runtime config with default values', () => {
119
- generateConfig(tempDir, ['stdio'], false);
120
-
121
- const runtimeConfigPath = path.join(tempDir, 'src', 'mcpi-runtime-config.ts');
122
- expect(fs.existsSync(runtimeConfigPath)).toBe(true);
123
-
124
- const runtimeConfigContent = fs.readFileSync(runtimeConfigPath, 'utf-8');
125
- expect(runtimeConfigContent).toContain('getRuntimeConfig');
126
- expect(runtimeConfigContent).toContain('buildBaseConfig');
127
- });
128
-
129
- it('should include default Node.js server configuration', () => {
130
- generateConfig(tempDir, ['stdio'], false);
131
-
132
- const runtimeConfigPath = path.join(tempDir, 'src', 'mcpi-runtime-config.ts');
133
- const runtimeConfigContent = fs.readFileSync(runtimeConfigPath, 'utf-8');
134
-
135
- expect(runtimeConfigContent).toContain('port:');
136
- expect(runtimeConfigContent).toContain('host:');
137
- expect(runtimeConfigContent).toContain('cors:');
138
- });
139
- });
140
-
141
- describe('Config merging', () => {
142
- it('should merge base config with Node.js-specific properties', () => {
143
- generateConfig(tempDir, ['stdio'], false);
144
-
145
- const runtimeConfigPath = path.join(tempDir, 'src', 'mcpi-runtime-config.ts');
146
- const runtimeConfigContent = fs.readFileSync(runtimeConfigPath, 'utf-8');
147
-
148
- // Should include base config properties
149
- expect(runtimeConfigContent).toContain('baseConfig');
150
- // Should include Node.js-specific properties
151
- expect(runtimeConfigContent).toContain('server:');
152
- expect(runtimeConfigContent).toContain('storage:');
153
- expect(runtimeConfigContent).toContain('nodeEnv:');
154
- });
155
-
156
- it('should merge identity config when not skipped', () => {
157
- generateConfig(tempDir, ['stdio'], false);
158
-
159
- const runtimeConfigPath = path.join(tempDir, 'src', 'mcpi-runtime-config.ts');
160
- const runtimeConfigContent = fs.readFileSync(runtimeConfigPath, 'utf-8');
161
-
162
- // The runtime config uses buildBaseConfig which includes identity
163
- // Check that buildBaseConfig is called (which includes identity)
164
- expect(runtimeConfigContent).toContain('buildBaseConfig');
165
- expect(runtimeConfigContent).toContain('baseConfig');
166
-
167
- // Also verify that xmcp.config.ts contains identity when not skipped
168
- const configPath = path.join(tempDir, 'xmcp.config.ts');
169
- const configContent = fs.readFileSync(configPath, 'utf-8');
170
- expect(configContent).toContain('identity');
171
- });
172
-
173
- it('should merge proofing config', () => {
174
- generateConfig(tempDir, ['stdio'], false);
175
-
176
- const runtimeConfigPath = path.join(tempDir, 'src', 'mcpi-runtime-config.ts');
177
- const runtimeConfigContent = fs.readFileSync(runtimeConfigPath, 'utf-8');
178
-
179
- // The runtime config uses buildBaseConfig which includes proofing
180
- // buildBaseConfig is imported and called, which internally includes proofing configuration
181
- expect(runtimeConfigContent).toContain('buildBaseConfig');
182
- expect(runtimeConfigContent).toContain('baseConfig');
183
- // The baseConfig spread includes proofing via buildBaseConfig
184
- expect(runtimeConfigContent).toContain('...baseConfig');
185
- });
186
-
187
- it('should merge delegation config', () => {
188
- generateConfig(tempDir, ['stdio'], false);
189
-
190
- const runtimeConfigPath = path.join(tempDir, 'src', 'mcpi-runtime-config.ts');
191
- const runtimeConfigContent = fs.readFileSync(runtimeConfigPath, 'utf-8');
192
-
193
- expect(runtimeConfigContent).toContain('delegation');
194
- });
195
- });
196
-
197
- describe('Validation rules', () => {
198
- it('should create valid TypeScript config file', () => {
199
- generateConfig(tempDir, ['stdio'], false);
200
-
201
- const configPath = path.join(tempDir, 'xmcp.config.ts');
202
- const configContent = fs.readFileSync(configPath, 'utf-8');
203
-
204
- // Should be valid TypeScript syntax
205
- expect(configContent).toContain('import');
206
- expect(configContent).toContain('export default');
207
- expect(configContent).toContain('const config');
208
- });
209
-
210
- it('should create valid TypeScript runtime config file', () => {
211
- generateConfig(tempDir, ['stdio'], false);
212
-
213
- const runtimeConfigPath = path.join(tempDir, 'src', 'mcpi-runtime-config.ts');
214
- const runtimeConfigContent = fs.readFileSync(runtimeConfigPath, 'utf-8');
215
-
216
- // Should be valid TypeScript syntax
217
- expect(runtimeConfigContent).toContain('import');
218
- expect(runtimeConfigContent).toContain('export function');
219
- expect(runtimeConfigContent).toContain('NodeRuntimeConfig');
220
- });
221
-
222
- it('should validate transport options', () => {
223
- // Empty transports array should still work
224
- generateConfig(tempDir, [], false);
225
-
226
- const configPath = path.join(tempDir, 'xmcp.config.ts');
227
- expect(fs.existsSync(configPath)).toBe(true);
228
- });
229
-
230
- it('should handle invalid transport values gracefully', () => {
231
- // Invalid transports should be ignored
232
- generateConfig(tempDir, ['invalid-transport' as any], false);
233
-
234
- const configPath = path.join(tempDir, 'xmcp.config.ts');
235
- const configContent = fs.readFileSync(configPath, 'utf-8');
236
-
237
- // Should not contain invalid transport
238
- expect(configContent).not.toContain('invalid-transport');
239
- });
240
-
241
- it('should validate file paths exist', () => {
242
- generateConfig(tempDir, ['stdio'], false);
243
-
244
- // Should create src directory for runtime config
245
- expect(fs.existsSync(path.join(tempDir, 'src'))).toBe(true);
246
- expect(fs.existsSync(path.join(tempDir, 'src', 'mcpi-runtime-config.ts'))).toBe(true);
247
- });
248
- });
249
-
250
- describe('Environment variable handling', () => {
251
- it('should use process.env in runtime config', () => {
252
- generateConfig(tempDir, ['stdio'], false);
253
-
254
- const runtimeConfigPath = path.join(tempDir, 'src', 'mcpi-runtime-config.ts');
255
- const runtimeConfigContent = fs.readFileSync(runtimeConfigPath, 'utf-8');
256
-
257
- expect(runtimeConfigContent).toContain('process.env');
258
- });
259
-
260
- it('should handle PORT environment variable', () => {
261
- generateConfig(tempDir, ['stdio'], false);
262
-
263
- const runtimeConfigPath = path.join(tempDir, 'src', 'mcpi-runtime-config.ts');
264
- const runtimeConfigContent = fs.readFileSync(runtimeConfigPath, 'utf-8');
265
-
266
- expect(runtimeConfigContent).toContain('PORT');
267
- expect(runtimeConfigContent).toContain('"3000"');
268
- });
269
-
270
- it('should handle HOST environment variable', () => {
271
- generateConfig(tempDir, ['stdio'], false);
272
-
273
- const runtimeConfigPath = path.join(tempDir, 'src', 'mcpi-runtime-config.ts');
274
- const runtimeConfigContent = fs.readFileSync(runtimeConfigPath, 'utf-8');
275
-
276
- expect(runtimeConfigContent).toContain('HOST');
277
- expect(runtimeConfigContent).toContain('"0.0.0.0"');
278
- });
279
-
280
- it('should handle NODE_ENV environment variable', () => {
281
- generateConfig(tempDir, ['stdio'], false);
282
-
283
- const runtimeConfigPath = path.join(tempDir, 'src', 'mcpi-runtime-config.ts');
284
- const runtimeConfigContent = fs.readFileSync(runtimeConfigPath, 'utf-8');
285
-
286
- expect(runtimeConfigContent).toContain('NODE_ENV');
287
- });
288
- });
289
-
290
- describe('File structure', () => {
291
- it('should create src directory if it does not exist', () => {
292
- generateConfig(tempDir, ['stdio'], false);
293
-
294
- expect(fs.existsSync(path.join(tempDir, 'src'))).toBe(true);
295
- });
296
-
297
- it('should not overwrite existing src directory', () => {
298
- const srcDir = path.join(tempDir, 'src');
299
- fs.ensureDirSync(srcDir);
300
- const existingFile = path.join(srcDir, 'existing.ts');
301
- fs.writeFileSync(existingFile, '// existing file');
302
-
303
- generateConfig(tempDir, ['stdio'], false);
304
-
305
- // Existing file should still exist
306
- expect(fs.existsSync(existingFile)).toBe(true);
307
- // Runtime config should also exist
308
- expect(fs.existsSync(path.join(srcDir, 'mcpi-runtime-config.ts'))).toBe(true);
309
- });
310
- });
311
- });
312
-