@kya-os/create-mcpi-app 1.7.17 → 1.7.20

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 (98) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test$colon$coverage.log +315 -0
  3. package/.turbo/turbo-test.log +95 -0
  4. package/CHANGELOG.md +372 -0
  5. package/IMPLEMENTATION_SUMMARY.md +108 -0
  6. package/REMEDIATION_PLAN.md +99 -0
  7. package/coverage/base.css +224 -0
  8. package/coverage/block-navigation.js +87 -0
  9. package/coverage/clover.xml +252 -0
  10. package/coverage/config-builder.ts.html +580 -0
  11. package/coverage/coverage-final.json +7 -0
  12. package/coverage/favicon.png +0 -0
  13. package/coverage/fetch-cloudflare-mcpi-template.ts.html +7006 -0
  14. package/coverage/generate-config.ts.html +436 -0
  15. package/coverage/generate-identity.ts.html +574 -0
  16. package/coverage/index.html +191 -0
  17. package/coverage/install.ts.html +322 -0
  18. package/coverage/prettify.css +1 -0
  19. package/coverage/prettify.js +2 -0
  20. package/coverage/sort-arrow-sprite.png +0 -0
  21. package/coverage/sorter.js +210 -0
  22. package/coverage/validate-project-structure.ts.html +466 -0
  23. package/dist/.tsbuildinfo +1 -1
  24. package/dist/helpers/__tests__/config-builder.spec.d.ts +8 -0
  25. package/dist/helpers/__tests__/config-builder.spec.d.ts.map +1 -0
  26. package/dist/helpers/__tests__/config-builder.spec.js +182 -0
  27. package/dist/helpers/__tests__/config-builder.spec.js.map +1 -0
  28. package/dist/helpers/config-builder.d.ts +58 -0
  29. package/dist/helpers/config-builder.d.ts.map +1 -0
  30. package/dist/helpers/config-builder.js +102 -0
  31. package/dist/helpers/config-builder.js.map +1 -0
  32. package/dist/helpers/create.d.ts +1 -0
  33. package/dist/helpers/create.d.ts.map +1 -1
  34. package/dist/helpers/create.js +2 -1
  35. package/dist/helpers/create.js.map +1 -1
  36. package/dist/helpers/fetch-cloudflare-mcpi-template.d.ts +1 -0
  37. package/dist/helpers/fetch-cloudflare-mcpi-template.d.ts.map +1 -1
  38. package/dist/helpers/fetch-cloudflare-mcpi-template.js +209 -174
  39. package/dist/helpers/fetch-cloudflare-mcpi-template.js.map +1 -1
  40. package/dist/helpers/fetch-mcpi-template.d.ts.map +1 -1
  41. package/dist/helpers/fetch-mcpi-template.js +18 -3
  42. package/dist/helpers/fetch-mcpi-template.js.map +1 -1
  43. package/dist/helpers/generate-config.d.ts.map +1 -1
  44. package/dist/helpers/generate-config.js +27 -40
  45. package/dist/helpers/generate-config.js.map +1 -1
  46. package/dist/helpers/install.js +5 -0
  47. package/dist/helpers/install.js.map +1 -1
  48. package/dist/index.js +2 -0
  49. package/dist/index.js.map +1 -1
  50. package/package.json +18 -9
  51. package/scripts/prepare-pack.js +47 -0
  52. package/scripts/validate-no-workspace.js +79 -0
  53. package/src/__tests__/cloudflare-template.test.ts +488 -0
  54. package/src/__tests__/helpers/fetch-cloudflare-mcpi-template.test.ts +337 -0
  55. package/src/__tests__/helpers/generate-config.test.ts +312 -0
  56. package/src/__tests__/helpers/generate-identity.test.ts +271 -0
  57. package/src/__tests__/helpers/install.test.ts +362 -0
  58. package/src/__tests__/helpers/validate-project-structure.test.ts +467 -0
  59. package/src/__tests__.bak/regression.test.ts +434 -0
  60. package/src/effects/index.ts +80 -0
  61. package/src/helpers/__tests__/config-builder.spec.ts +231 -0
  62. package/src/helpers/apply-identity-preset.ts +209 -0
  63. package/src/helpers/config-builder.ts +165 -0
  64. package/src/helpers/copy-template.ts +11 -0
  65. package/src/helpers/create.ts +239 -0
  66. package/src/helpers/fetch-cloudflare-mcpi-template.ts +2311 -0
  67. package/src/helpers/fetch-cloudflare-template.ts +361 -0
  68. package/src/helpers/fetch-mcpi-template.ts +236 -0
  69. package/src/helpers/fetch-xmcp-template.ts +153 -0
  70. package/src/helpers/generate-config.ts +117 -0
  71. package/src/helpers/generate-identity.ts +163 -0
  72. package/src/helpers/identity-manager.ts +186 -0
  73. package/src/helpers/install.ts +79 -0
  74. package/src/helpers/rename.ts +17 -0
  75. package/src/helpers/validate-project-structure.ts +127 -0
  76. package/src/index.ts +480 -0
  77. package/src/utils/check-node.ts +17 -0
  78. package/src/utils/is-folder-empty.ts +60 -0
  79. package/src/utils/validate-project-name.ts +132 -0
  80. package/test-cloudflare/README.md +164 -0
  81. package/test-cloudflare/package.json +28 -0
  82. package/test-cloudflare/src/index.ts +340 -0
  83. package/test-cloudflare/src/tools/greet.ts +19 -0
  84. package/test-cloudflare/tests/cache-invalidation.test.ts +410 -0
  85. package/test-cloudflare/tests/cors-security.test.ts +349 -0
  86. package/test-cloudflare/tests/delegation.test.ts +335 -0
  87. package/test-cloudflare/tests/do-routing.test.ts +314 -0
  88. package/test-cloudflare/tests/integration.test.ts +205 -0
  89. package/test-cloudflare/tests/session-management.test.ts +359 -0
  90. package/test-cloudflare/tsconfig.json +22 -0
  91. package/test-cloudflare/vitest.config.ts +9 -0
  92. package/test-cloudflare/wrangler.toml +37 -0
  93. package/test-node/README.md +44 -0
  94. package/test-node/package.json +23 -0
  95. package/test-node/src/tools/greet.ts +25 -0
  96. package/test-node/xmcp.config.ts +20 -0
  97. package/tsconfig.json +26 -0
  98. package/vitest.config.ts +14 -0
@@ -0,0 +1,361 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import chalk from "chalk";
4
+ import { fileURLToPath } from "url";
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ interface CloudflareTemplateOptions {
10
+ verifierVersion?: string;
11
+ packageManager?: string;
12
+ }
13
+
14
+ /**
15
+ * Scaffold Cloudflare Worker verifier project
16
+ * This creates a standalone Worker verifier with KV nonce caching
17
+ */
18
+ export async function fetchCloudflareTemplate(
19
+ projectPath: string,
20
+ options: CloudflareTemplateOptions = {}
21
+ ): Promise<void> {
22
+ const { verifierVersion = "^1.3.0", packageManager = "npm" } = options;
23
+
24
+ try {
25
+ console.log(
26
+ chalk.blue(`📦 Setting up Cloudflare Worker verifier with @kya-os/verifier@${verifierVersion}...`)
27
+ );
28
+
29
+ // Create package.json for Cloudflare Worker
30
+ const packageJson = {
31
+ name: path.basename(projectPath),
32
+ version: "1.0.0",
33
+ type: "module",
34
+ private: true,
35
+ scripts: {
36
+ dev: "wrangler dev",
37
+ deploy: "wrangler deploy",
38
+ "deploy:production": "wrangler deploy --env production",
39
+ "kv:create": "wrangler kv namespace create NONCE_CACHE",
40
+ "kv:create:preview": "wrangler kv namespace create NONCE_CACHE --preview",
41
+ tail: "wrangler tail",
42
+ test: "vitest",
43
+ },
44
+ dependencies: {
45
+ "@kya-os/verifier": verifierVersion,
46
+ },
47
+ devDependencies: {
48
+ "@cloudflare/workers-types": "^4.20240925.0",
49
+ "@cloudflare/vitest-pool-workers": "^0.5.0",
50
+ typescript: "^5.6.2",
51
+ vitest: "^2.1.5",
52
+ wrangler: "^4.42.2",
53
+ },
54
+ };
55
+
56
+ // Write package.json
57
+ fs.ensureDirSync(projectPath);
58
+ fs.writeJsonSync(path.join(projectPath, "package.json"), packageJson, { spaces: 2 });
59
+
60
+ // Create src directory
61
+ const srcDir = path.join(projectPath, "src");
62
+ fs.ensureDirSync(srcDir);
63
+
64
+ // Create Worker implementation
65
+ const workerContent = `import {
66
+ verifyWorker,
67
+ applyVerificationToResponse,
68
+ createConfigFromEnv,
69
+ type WorkerEnv,
70
+ } from "@kya-os/verifier/worker";
71
+
72
+ export default {
73
+ async fetch(request: Request, env: WorkerEnv): Promise<Response> {
74
+ const url = new URL(request.url);
75
+
76
+ // Health check endpoint
77
+ if (url.pathname === "/health") {
78
+ return new Response(
79
+ JSON.stringify({
80
+ status: "healthy",
81
+ timestamp: new Date().toISOString(),
82
+ }),
83
+ {
84
+ headers: { "Content-Type": "application/json" },
85
+ }
86
+ );
87
+ }
88
+
89
+ // Verification endpoint
90
+ if (url.pathname === "/verify") {
91
+ // Create config from environment variables
92
+ const config = createConfigFromEnv(env, {
93
+ allowMockData: false,
94
+ });
95
+
96
+ // Verify the proof
97
+ const result = await verifyWorker(request, config);
98
+
99
+ // If verification failed, return error response
100
+ if (!result.success) {
101
+ return applyVerificationToResponse(result);
102
+ }
103
+
104
+ // Success - extract agent context
105
+ const agent = result.agentContext;
106
+
107
+ // Check required scopes (example)
108
+ const requiredScopes = ["cart:add", "checkout"];
109
+ const hasRequiredScope = requiredScopes.some((scope) =>
110
+ agent?.scopes?.includes(scope)
111
+ );
112
+
113
+ if (!hasRequiredScope) {
114
+ return new Response(
115
+ JSON.stringify({
116
+ verified: false,
117
+ error: "Insufficient permissions",
118
+ requiredScopes,
119
+ providedScopes: agent?.scopes || [],
120
+ }),
121
+ {
122
+ status: 403,
123
+ headers: { "Content-Type": "application/json" },
124
+ }
125
+ );
126
+ }
127
+
128
+ // Success response with agent context
129
+ return new Response(
130
+ JSON.stringify({
131
+ verified: true,
132
+ agent: {
133
+ did: agent?.did,
134
+ kid: agent?.kid,
135
+ scopes: agent?.scopes,
136
+ session: agent?.session,
137
+ },
138
+ timestamp: new Date().toISOString(),
139
+ }),
140
+ {
141
+ headers: { "Content-Type": "application/json" },
142
+ }
143
+ );
144
+ }
145
+
146
+ return new Response("Not Found", { status: 404 });
147
+ },
148
+ };
149
+ `;
150
+ fs.writeFileSync(path.join(srcDir, "index.ts"), workerContent);
151
+
152
+ // Create wrangler.toml
153
+ const wranglerContent = `#:schema node_modules/wrangler/config-schema.json
154
+ name = "${path.basename(projectPath)}"
155
+ main = "src/index.ts"
156
+ compatibility_date = "2024-09-25"
157
+ node_compat = true
158
+
159
+ # KV Namespace for nonce cache
160
+ # Run: wrangler kv:namespace create NONCE_CACHE
161
+ # Then replace the id below with the output
162
+ [[kv_namespaces]]
163
+ binding = "NONCE_CACHE"
164
+ id = "YOUR_KV_NAMESPACE_ID"
165
+
166
+ # Environment variables (non-sensitive)
167
+ [vars]
168
+ KYA_API_URL = "https://knowthat.ai"
169
+ XMCP_I_TS_SKEW_SEC = "120"
170
+ XMCP_I_SESSION_TTL = "1800"
171
+
172
+ # Production environment
173
+ [env.production]
174
+ name = "${path.basename(projectPath)}-production"
175
+ vars = { KYA_API_URL = "https://knowthat.ai", XMCP_I_TS_SKEW_SEC = "60" }
176
+
177
+ [[env.production.kv_namespaces]]
178
+ binding = "NONCE_CACHE"
179
+ id = "YOUR_PRODUCTION_KV_NAMESPACE_ID"
180
+ `;
181
+ fs.writeFileSync(path.join(projectPath, "wrangler.toml"), wranglerContent);
182
+
183
+ // Create tsconfig.json
184
+ const tsconfigContent = {
185
+ compilerOptions: {
186
+ target: "ES2022",
187
+ module: "ES2022",
188
+ lib: ["ES2022"],
189
+ types: ["@cloudflare/workers-types"],
190
+ moduleResolution: "bundler",
191
+ resolveJsonModule: true,
192
+ allowSyntheticDefaultImports: true,
193
+ esModuleInterop: true,
194
+ strict: true,
195
+ skipLibCheck: true,
196
+ forceConsistentCasingInFileNames: true,
197
+ },
198
+ include: ["src/**/*"],
199
+ };
200
+ fs.writeJsonSync(path.join(projectPath, "tsconfig.json"), tsconfigContent, { spaces: 2 });
201
+
202
+ // Create .gitignore
203
+ const gitignoreContent = `node_modules/
204
+ dist/
205
+ .wrangler/
206
+ .dev.vars
207
+ .env
208
+ .env.local
209
+ *.log
210
+ `;
211
+ fs.writeFileSync(path.join(projectPath, ".gitignore"), gitignoreContent);
212
+
213
+ // Create README.md
214
+ const readmeContent = `# ${path.basename(projectPath)}
215
+
216
+ MCP-I proof verifier deployed on Cloudflare Workers with KV nonce caching.
217
+
218
+ ## Quick Start
219
+
220
+ ### 1. Install Dependencies
221
+
222
+ \`\`\`bash
223
+ ${packageManager} install
224
+ \`\`\`
225
+
226
+ ### 2. Create KV Namespace
227
+
228
+ \`\`\`bash
229
+ ${packageManager === "npm" ? "npm run" : packageManager} kv:create
230
+ \`\`\`
231
+
232
+ Copy the \`id\` from the output and update \`wrangler.toml\`:
233
+
234
+ \`\`\`toml
235
+ [[kv_namespaces]]
236
+ binding = "NONCE_CACHE"
237
+ id = "your-kv-namespace-id-here" # ← Update this
238
+ \`\`\`
239
+
240
+ ### 3. Test Locally
241
+
242
+ \`\`\`bash
243
+ ${packageManager === "npm" ? "npm run" : packageManager} dev
244
+ \`\`\`
245
+
246
+ Visit http://localhost:8787/health to verify it's running.
247
+
248
+ ### 4. Deploy to Cloudflare
249
+
250
+ \`\`\`bash
251
+ # Login to Cloudflare (first time only)
252
+ npx wrangler login
253
+
254
+ # Deploy to development
255
+ ${packageManager === "npm" ? "npm run" : packageManager} deploy
256
+
257
+ # Deploy to production
258
+ ${packageManager === "npm" ? "npm run" : packageManager} deploy:production
259
+ \`\`\`
260
+
261
+ ## API Endpoints
262
+
263
+ ### \`GET /health\`
264
+ Health check endpoint.
265
+
266
+ **Response:**
267
+ \`\`\`json
268
+ {
269
+ "status": "healthy",
270
+ "timestamp": "2024-10-09T12:00:00.000Z"
271
+ }
272
+ \`\`\`
273
+
274
+ ### \`POST /verify\`
275
+ Verify MCP-I cryptographic proof.
276
+
277
+ **Request:**
278
+ \`\`\`json
279
+ {
280
+ "proof": {
281
+ "agentDid": "did:web:example.com",
282
+ "timestamp": 1234567890,
283
+ "nonce": "abc123",
284
+ "signature": "...",
285
+ "publicKey": "..."
286
+ },
287
+ "requiredScopes": ["cart:add"]
288
+ }
289
+ \`\`\`
290
+
291
+ **Response (Success):**
292
+ \`\`\`json
293
+ {
294
+ "verified": true,
295
+ "agentDid": "did:web:example.com",
296
+ "timestamp": "2024-10-09T12:00:00.000Z"
297
+ }
298
+ \`\`\`
299
+
300
+ **Response (Failed):**
301
+ \`\`\`json
302
+ {
303
+ "verified": false,
304
+ "error": "Invalid signature"
305
+ }
306
+ \`\`\`
307
+
308
+ ## Configuration
309
+
310
+ ### Environment Variables (wrangler.toml)
311
+
312
+ - \`KYA_API_URL\` - KYA API endpoint (default: https://knowthat.ai)
313
+ - \`XMCP_I_TS_SKEW_SEC\` - Clock skew tolerance in seconds (default: 120)
314
+ - \`XMCP_I_SESSION_TTL\` - Session timeout in seconds (default: 1800)
315
+
316
+ ### KV Namespace
317
+
318
+ The Worker uses Cloudflare KV for distributed nonce caching to prevent replay attacks.
319
+
320
+ Create separate namespaces for development and production:
321
+
322
+ \`\`\`bash
323
+ # Development
324
+ ${packageManager === "npm" ? "npm run" : packageManager} kv:create
325
+
326
+ # Production (after deploying once)
327
+ wrangler kv:namespace create NONCE_CACHE --env production
328
+ \`\`\`
329
+
330
+ ## Testing
331
+
332
+ Send a test request:
333
+
334
+ \`\`\`bash
335
+ curl -X POST https://your-worker.workers.dev/verify \\
336
+ -H "Content-Type: application/json" \\
337
+ -d '{
338
+ "proof": {
339
+ "agentDid": "did:web:example.com",
340
+ "timestamp": 1234567890,
341
+ "nonce": "test123",
342
+ "signature": "...",
343
+ "publicKey": "..."
344
+ }
345
+ }'
346
+ \`\`\`
347
+
348
+ ## Learn More
349
+
350
+ - [MCP-I Documentation](https://github.com/kya-os/xmcp-i)
351
+ - [Cloudflare Workers Docs](https://developers.cloudflare.com/workers/)
352
+ - [Cloudflare KV Docs](https://developers.cloudflare.com/kv/)
353
+ `;
354
+ fs.writeFileSync(path.join(projectPath, "README.md"), readmeContent);
355
+
356
+ console.log(chalk.green("✅ Cloudflare Worker verifier structure created"));
357
+ } catch (error) {
358
+ console.error(chalk.red("Failed to set up Cloudflare Worker verifier:"), error);
359
+ throw error;
360
+ }
361
+ }
@@ -0,0 +1,236 @@
1
+ import { execSync } from "child_process";
2
+ import fs from "fs-extra";
3
+ import path from "path";
4
+ import os from "os";
5
+ import chalk from "chalk";
6
+ import { generateIdentity } from "./generate-identity.js";
7
+
8
+ interface ScaffoldTemplateOptions {
9
+ mcpiVersion?: string;
10
+ packageManager?: string;
11
+ skipIdentity?: boolean;
12
+ }
13
+
14
+ /**
15
+ * Scaffold MCP-I project using @kya-os/mcp-i package
16
+ * This creates a standalone MCP server with built-in identity features
17
+ */
18
+ export async function fetchMCPITemplate(
19
+ projectPath: string,
20
+ options: ScaffoldTemplateOptions = {}
21
+ ): Promise<void> {
22
+ const {
23
+ mcpiVersion = "^1.3.2",
24
+ packageManager = "npm",
25
+ skipIdentity = false,
26
+ } = options;
27
+
28
+ try {
29
+ console.log(
30
+ chalk.blue(
31
+ `📦 Setting up MCP-I project with @kya-os/mcp-i@${mcpiVersion}...`
32
+ )
33
+ );
34
+
35
+ // Create package.json matching our working test-mcpi structure
36
+ const packageJson = {
37
+ name: path.basename(projectPath),
38
+ version: "0.1.0",
39
+ dependencies: {
40
+ "@kya-os/cli": "^1.2.7",
41
+ "@kya-os/mcp-i": mcpiVersion,
42
+ zod: "^3.24.4",
43
+ },
44
+ scripts: {
45
+ dev: "mcpi dev",
46
+ build: "mcpi build",
47
+ start: "mcpi start",
48
+ init: "mcpi init",
49
+ register: "mcpi register",
50
+ "keys:rotate": "mcpi rotate",
51
+ "identity:clean": "rm -rf .mcpi",
52
+ status: "mcpi status",
53
+ },
54
+ devDependencies: {
55
+ "@modelcontextprotocol/inspector": "^0.16.6",
56
+ "swc-loader": "^0.2.6",
57
+ },
58
+ };
59
+
60
+ // Write package.json
61
+ fs.ensureDirSync(projectPath);
62
+ fs.writeJsonSync(path.join(projectPath, "package.json"), packageJson, {
63
+ spaces: 2,
64
+ });
65
+
66
+ // Create basic MCP-I project structure
67
+ const srcDir = path.join(projectPath, "src");
68
+ const toolsDir = path.join(srcDir, "tools");
69
+
70
+ // Create required directories
71
+ fs.ensureDirSync(toolsDir);
72
+
73
+ // Optional directories (not created by default - users can add as needed)
74
+ // These are typically only needed for:
75
+ // - scenarios/: Demo/testing scenarios (like bouncer-demo)
76
+ // - lib/: Shared utilities (if project grows complex)
77
+ // - scripts/: Build/utility scripts (if needed)
78
+ // - docs/: Documentation (if maintaining separate docs)
79
+
80
+ // Create a basic greet tool (MCP-I default)
81
+ // CRITICAL: Import from @kya-os/mcp-i (not xmcp)
82
+ const greetToolContent = `import { z } from "zod";
83
+ import { type InferSchema, type ToolMetadata } from "@kya-os/mcp-i";
84
+
85
+ // Define the schema for tool parameters
86
+ export const schema = {
87
+ name: z.string().describe("The name of the user to greet"),
88
+ };
89
+
90
+ // Define tool metadata
91
+ export const metadata: ToolMetadata = {
92
+ name: "greet",
93
+ description: "Greet the user",
94
+ };
95
+
96
+ // Tool implementation
97
+ export default async function greet({ name }: InferSchema<typeof schema>) {
98
+ return {
99
+ content: [
100
+ {
101
+ type: "text" as const,
102
+ text: \`Hello, \${name}!\`,
103
+ },
104
+ ],
105
+ };
106
+ }
107
+ `;
108
+
109
+ fs.writeFileSync(path.join(toolsDir, "greet.ts"), greetToolContent);
110
+
111
+ // IMPORTANT: DO NOT create tools/index.ts
112
+ // This would cause duplicate tool registration in webpack bundling
113
+
114
+ // Create xmcp.config.ts with identity features enabled (matching test-mcpi)
115
+ const configContent = `import type { XmcpConfig } from "@kya-os/mcp-i";
116
+
117
+ const config: XmcpConfig = {
118
+ // Point to the tools directory
119
+ paths: {
120
+ tools: "./src/tools",
121
+ },
122
+
123
+ // Enable MCP-I identity features
124
+ identity: {
125
+ enabled: true,
126
+ environment: "development",
127
+ debug: true,
128
+ },
129
+
130
+ // Enable STDIO transport for Claude Desktop
131
+ stdio: true,
132
+ };
133
+
134
+ export default config;
135
+ `;
136
+ fs.writeFileSync(path.join(projectPath, "xmcp.config.ts"), configContent);
137
+
138
+ // Create .gitignore
139
+ const gitignoreContent = `node_modules/
140
+ dist/
141
+ .env
142
+ .env.local
143
+ .mcpi/
144
+ `;
145
+ fs.writeFileSync(path.join(projectPath, ".gitignore"), gitignoreContent);
146
+
147
+ // Create README.md with quick start instructions
148
+ const readmeContent = `# ${path.basename(projectPath)}
149
+
150
+ MCP-I server with cryptographic identity built-in.
151
+
152
+ ## Getting Started
153
+
154
+ 1. Install dependencies:
155
+ \`\`\`bash
156
+ npm install
157
+ \`\`\`
158
+
159
+ 2. Initialize identity (generates cryptographic keys):
160
+ \`\`\`bash
161
+ npm run init
162
+ \`\`\`
163
+
164
+ 3. Start development server:
165
+ \`\`\`bash
166
+ npm run dev
167
+ \`\`\`
168
+
169
+ ## Scripts
170
+
171
+ - \`npm run dev\` - Start development server with hot reload
172
+ - \`npm run build\` - Build production bundle
173
+ - \`npm run start\` - Start production server
174
+ - \`npm run init\` - Initialize agent identity
175
+ - \`npm run status\` - Check identity and server status
176
+ - \`npm run keys:rotate\` - Rotate cryptographic keys
177
+
178
+ ## Project Structure
179
+
180
+ \`\`\`
181
+ ├── src/
182
+ │ └── tools/ # MCP tools
183
+ │ └── greet.ts # Example tool
184
+ ├── xmcp.config.ts # MCP-I configuration
185
+ └── .mcpi/ # Identity files (git-ignored)
186
+ \`\`\`
187
+
188
+ **Optional directories** (add as needed):
189
+ - \`src/scenarios/\` - Demo/testing scenarios
190
+ - \`src/lib/\` - Shared utilities
191
+ - \`scripts/\` - Build/utility scripts
192
+ - \`docs/\` - Documentation
193
+
194
+ ## Learn More
195
+
196
+ - [MCP-I Documentation](https://github.com/modelcontextprotocol-identity/mcp-i)
197
+ - [Model Context Protocol](https://modelcontextprotocol.io)
198
+ `;
199
+ fs.writeFileSync(path.join(projectPath, "README.md"), readmeContent);
200
+
201
+ console.log(chalk.green("✅ MCP-I project structure created"));
202
+
203
+ // Generate persistent identity for Node.js MCP-I server
204
+ if (!skipIdentity) {
205
+ console.log(chalk.cyan("🔐 Generating persistent identity..."));
206
+ try {
207
+ const identity = await generateIdentity();
208
+
209
+ // Create .mcpi directory
210
+ const mcpiDir = path.join(projectPath, ".mcpi");
211
+ fs.ensureDirSync(mcpiDir);
212
+
213
+ // Save identity to .mcpi/identity.json
214
+ const identityPath = path.join(mcpiDir, "identity.json");
215
+ fs.writeJsonSync(identityPath, identity, { spaces: 2 });
216
+
217
+ console.log(chalk.green("✅ Generated persistent identity"));
218
+ console.log(chalk.dim(` DID: ${identity.did}`));
219
+ console.log(chalk.dim(` Saved to: .mcpi/identity.json`));
220
+ } catch (error: any) {
221
+ console.log(
222
+ chalk.yellow("⚠️ Failed to generate identity:"),
223
+ error.message
224
+ );
225
+ console.log(
226
+ chalk.dim(
227
+ " You can generate identity later by running: npm run init"
228
+ )
229
+ );
230
+ }
231
+ }
232
+ } catch (error) {
233
+ console.error(chalk.red("Failed to set up MCP-I project:"), error);
234
+ throw error;
235
+ }
236
+ }