@kya-os/create-mcpi-app 1.8.45 → 1.8.47

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.
@@ -0,0 +1,800 @@
1
+ /**
2
+ * Programmatic Cloudflare Project File Generation
3
+ *
4
+ * This module generates Cloudflare MCP-I project files in-memory,
5
+ * suitable for committing to GitHub via API (one-click deployment).
6
+ *
7
+ * Unlike fetchCloudflareMcpiTemplate (which writes to disk), this function
8
+ * returns file contents that can be committed programmatically.
9
+ *
10
+ * @module @kya-os/create-mcpi-app/helpers/generate-cloudflare-files
11
+ */
12
+ import crypto from "crypto";
13
+ import { generateIdentity, } from "./generate-identity.js";
14
+ /**
15
+ * KV binding names (matches env-mapper.ts for consistency)
16
+ */
17
+ const KV_BINDING_NAMES = [
18
+ "NONCE_CACHE",
19
+ "PROOF_ARCHIVE",
20
+ "IDENTITY_STORAGE",
21
+ "DELEGATION_STORAGE",
22
+ "TOOL_PROTECTION_KV",
23
+ ];
24
+ /**
25
+ * Generate Cloudflare MCP-I project files in-memory
26
+ *
27
+ * This function generates all files needed for a Cloudflare Workers MCP-I project
28
+ * but returns them as strings instead of writing to disk. This enables
29
+ * programmatic deployment via GitHub API.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const result = await generateCloudflareProjectFiles({
34
+ * projectName: "my-agent",
35
+ * agentShieldProjectId: "my-project-abc123",
36
+ * });
37
+ *
38
+ * // Commit files to GitHub
39
+ * for (const file of result.files) {
40
+ * await octokit.repos.createOrUpdateFileContents({
41
+ * owner, repo,
42
+ * path: file.path,
43
+ * content: Buffer.from(file.content).toString("base64"),
44
+ * });
45
+ * }
46
+ *
47
+ * // Add secrets to GitHub
48
+ * await octokit.actions.createOrUpdateRepoSecret({
49
+ * owner, repo,
50
+ * secret_name: "MCP_IDENTITY_PRIVATE_KEY",
51
+ * encrypted_value: await encrypt(result.secrets.MCP_IDENTITY_PRIVATE_KEY),
52
+ * });
53
+ * ```
54
+ */
55
+ export async function generateCloudflareProjectFiles(options) {
56
+ const { projectName, template = "blank", agentShieldProjectId, agentShieldApiUrl = "https://kya.vouched.id", agentShieldApiKey, skipIdentity = false, } = options;
57
+ const projectNameUpper = projectName.toUpperCase().replace(/-/g, "_");
58
+ const pascalCaseName = toPascalCase(projectName);
59
+ // Generate identity
60
+ let identity;
61
+ if (skipIdentity) {
62
+ identity = {
63
+ did: "did:key:zTestMockIdentity",
64
+ kid: "did:key:zTestMockIdentity#key-1",
65
+ privateKey: "mock-private-key",
66
+ publicKey: "mock-public-key",
67
+ createdAt: new Date().toISOString(),
68
+ type: "development",
69
+ };
70
+ }
71
+ else {
72
+ identity = await generateIdentity();
73
+ }
74
+ // Generate OAuth encryption secret
75
+ const oauthEncryptionSecret = crypto.randomBytes(32).toString("hex");
76
+ const files = [];
77
+ // 1. package.json
78
+ files.push({
79
+ path: "package.json",
80
+ content: JSON.stringify({
81
+ name: projectName,
82
+ version: "0.1.0",
83
+ private: true,
84
+ scripts: {
85
+ deploy: "wrangler deploy",
86
+ dev: "wrangler dev",
87
+ start: "wrangler dev",
88
+ test: "vitest",
89
+ "cf-typegen": "wrangler types",
90
+ },
91
+ dependencies: {
92
+ "@kya-os/mcp-i-cloudflare": "^1.6.45",
93
+ "@modelcontextprotocol/sdk": "1.24.0",
94
+ agents: "0.2.30",
95
+ hono: "4.10.3",
96
+ },
97
+ devDependencies: {
98
+ "@cloudflare/vitest-pool-workers": "^0.5.41",
99
+ "@cloudflare/workers-types": "^4.20251126.0",
100
+ "@types/node": "^20.8.3",
101
+ typescript: "^5.5.2",
102
+ vitest: "^2.0.5",
103
+ wrangler: "^4.53.0",
104
+ },
105
+ }, null, 2),
106
+ encoding: "utf-8",
107
+ });
108
+ // 2. wrangler.toml (NO private key - that goes to GitHub Secrets)
109
+ files.push({
110
+ path: "wrangler.toml",
111
+ content: generateWranglerToml({
112
+ projectName,
113
+ projectNameUpper,
114
+ pascalCaseName,
115
+ agentDid: identity.did,
116
+ publicKey: identity.publicKey,
117
+ agentShieldProjectId,
118
+ agentShieldApiUrl,
119
+ }),
120
+ encoding: "utf-8",
121
+ });
122
+ // 3. .dev.vars.example (template for local dev, not the actual secrets)
123
+ files.push({
124
+ path: ".dev.vars.example",
125
+ content: `# Copy this to .dev.vars for local development
126
+ # For production, secrets are managed via GitHub Secrets + Cloudflare
127
+
128
+ # Agent Identity (DO NOT COMMIT .dev.vars)
129
+ MCP_IDENTITY_PRIVATE_KEY="your-private-key-here"
130
+
131
+ # OAuth Encryption Secret (required for OAuth/delegation flows)
132
+ OAUTH_ENCRYPTION_SECRET="your-oauth-secret-here"
133
+
134
+ # AgentShield API Key
135
+ AGENTSHIELD_API_KEY="sk_your_api_key_here"
136
+
137
+ # Admin API Key (optional, falls back to AGENTSHIELD_API_KEY)
138
+ # ADMIN_API_KEY="sk_your_admin_key_here"
139
+ `,
140
+ encoding: "utf-8",
141
+ });
142
+ // 4. .gitignore
143
+ files.push({
144
+ path: ".gitignore",
145
+ content: `node_modules/
146
+ dist/
147
+ .wrangler/
148
+ .dev.vars
149
+ *.log
150
+ .DS_Store
151
+ `,
152
+ encoding: "utf-8",
153
+ });
154
+ // 5. tsconfig.json
155
+ files.push({
156
+ path: "tsconfig.json",
157
+ content: JSON.stringify({
158
+ compilerOptions: {
159
+ target: "esnext",
160
+ module: "esnext",
161
+ moduleResolution: "bundler",
162
+ types: ["@cloudflare/workers-types", "vitest/globals"],
163
+ strict: true,
164
+ skipLibCheck: true,
165
+ noEmit: true,
166
+ },
167
+ include: ["src/**/*"],
168
+ exclude: ["node_modules"],
169
+ }, null, 2),
170
+ encoding: "utf-8",
171
+ });
172
+ // 6. src/index.ts
173
+ files.push({
174
+ path: "src/index.ts",
175
+ content: generateIndexTs(projectName, projectNameUpper, pascalCaseName),
176
+ encoding: "utf-8",
177
+ });
178
+ // 7. src/agent.ts
179
+ files.push({
180
+ path: "src/agent.ts",
181
+ content: generateAgentTs(projectName, projectNameUpper, pascalCaseName),
182
+ encoding: "utf-8",
183
+ });
184
+ // 8. src/mcpi-runtime-config.ts
185
+ files.push({
186
+ path: "src/mcpi-runtime-config.ts",
187
+ content: generateRuntimeConfigTs(template),
188
+ encoding: "utf-8",
189
+ });
190
+ // 9. src/tools/greet.ts (always included)
191
+ files.push({
192
+ path: "src/tools/greet.ts",
193
+ content: generateGreetToolTs(projectName),
194
+ encoding: "utf-8",
195
+ });
196
+ // 10. Add template-specific tools
197
+ if (template === "ecommerce") {
198
+ files.push(...generateEcommerceTools());
199
+ }
200
+ else if (template === "hardware-world") {
201
+ files.push(...generateHardwareWorldTools());
202
+ }
203
+ // 11. GitHub Actions workflow
204
+ files.push({
205
+ path: ".github/workflows/deploy.yml",
206
+ content: generateDeployWorkflow(),
207
+ encoding: "utf-8",
208
+ });
209
+ // 12. README.md
210
+ files.push({
211
+ path: "README.md",
212
+ content: generateReadme(projectName, agentShieldProjectId),
213
+ encoding: "utf-8",
214
+ });
215
+ // Build secrets object - always include identity and OAuth secrets
216
+ const secrets = {
217
+ MCP_IDENTITY_PRIVATE_KEY: identity.privateKey,
218
+ OAUTH_ENCRYPTION_SECRET: oauthEncryptionSecret,
219
+ };
220
+ // Include AgentShield API key if provided
221
+ if (agentShieldApiKey) {
222
+ secrets.AGENTSHIELD_API_KEY = agentShieldApiKey;
223
+ }
224
+ return {
225
+ files,
226
+ identity: {
227
+ did: identity.did,
228
+ publicKey: identity.publicKey,
229
+ privateKey: identity.privateKey,
230
+ },
231
+ secrets,
232
+ };
233
+ }
234
+ // ============================================================================
235
+ // Helper Functions
236
+ // ============================================================================
237
+ function toPascalCase(str) {
238
+ const result = str
239
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => word.toUpperCase())
240
+ .replace(/\s+/g, "")
241
+ .replace(/-/g, "")
242
+ .replace(/[^a-zA-Z0-9]/g, "");
243
+ let className = result || "Project";
244
+ if (/^\d/.test(className)) {
245
+ className = "_" + className;
246
+ }
247
+ return className;
248
+ }
249
+ function generateWranglerToml(opts) {
250
+ const { projectName, projectNameUpper, pascalCaseName, agentDid, publicKey, agentShieldProjectId, agentShieldApiUrl, } = opts;
251
+ return `#:schema node_modules/wrangler/config-schema.json
252
+ name = "${projectName}"
253
+ main = "src/index.ts"
254
+ compatibility_date = "2025-01-01"
255
+ compatibility_flags = ["nodejs_compat"]
256
+
257
+ # Durable Object binding for MCP Agent state
258
+ [[durable_objects.bindings]]
259
+ name = "MCP_OBJECT"
260
+ class_name = "${pascalCaseName}MCP"
261
+
262
+ [[migrations]]
263
+ tag = "v1"
264
+ new_sqlite_classes = ["${pascalCaseName}MCP"]
265
+
266
+ # ═══════════════════════════════════════════════════════════════════════════════
267
+ # KV NAMESPACES - Auto-provisioned by Wrangler v4.45+
268
+ # ═══════════════════════════════════════════════════════════════════════════════
269
+ # Just run: npm run deploy (KV namespaces are created automatically!)
270
+
271
+ [[kv_namespaces]]
272
+ binding = "${projectNameUpper}_${KV_BINDING_NAMES[0]}"
273
+
274
+ [[kv_namespaces]]
275
+ binding = "${projectNameUpper}_${KV_BINDING_NAMES[1]}"
276
+
277
+ [[kv_namespaces]]
278
+ binding = "${projectNameUpper}_${KV_BINDING_NAMES[2]}"
279
+
280
+ [[kv_namespaces]]
281
+ binding = "${projectNameUpper}_${KV_BINDING_NAMES[3]}"
282
+
283
+ [[kv_namespaces]]
284
+ binding = "${projectNameUpper}_${KV_BINDING_NAMES[4]}"
285
+
286
+ [vars]
287
+ # Agent DID (public identifier - safe to commit)
288
+ MCP_IDENTITY_AGENT_DID = "${agentDid}"
289
+
290
+ # Public identity key (safe to commit)
291
+ MCP_IDENTITY_PUBLIC_KEY = "${publicKey}"
292
+
293
+ # CORS origins
294
+ ALLOWED_ORIGINS = "https://claude.ai,https://app.anthropic.com"
295
+
296
+ # Durable Object routing
297
+ DO_ROUTING_STRATEGY = "session"
298
+ DO_SHARD_COUNT = "10"
299
+
300
+ XMCP_I_TS_SKEW_SEC = "120"
301
+ XMCP_I_SESSION_TTL = "1800"
302
+
303
+ # AgentShield Integration
304
+ AGENTSHIELD_API_URL = "${agentShieldApiUrl}"
305
+ ${agentShieldProjectId ? `AGENTSHIELD_PROJECT_ID = "${agentShieldProjectId}"` : '# AGENTSHIELD_PROJECT_ID = "your-project-id"'}
306
+
307
+ MCPI_ENV = "development"
308
+
309
+ # ═══════════════════════════════════════════════════════════════════════════════
310
+ # SECRETS (managed via GitHub Secrets + wrangler secret)
311
+ # ═══════════════════════════════════════════════════════════════════════════════
312
+ # These are automatically deployed via GitHub Actions workflow.
313
+ # For local development, copy .dev.vars.example to .dev.vars
314
+ `;
315
+ }
316
+ function generateIndexTs(projectName, projectNameUpper, pascalCaseName) {
317
+ return `import { createMCPIApp } from "@kya-os/mcp-i-cloudflare";
318
+ import { ${pascalCaseName}MCP } from "./agent";
319
+ import { getRuntimeConfig } from "./mcpi-runtime-config";
320
+
321
+ export default createMCPIApp({
322
+ AgentClass: ${pascalCaseName}MCP,
323
+ getRuntimeConfig,
324
+ envPrefix: "${projectNameUpper}",
325
+ });
326
+
327
+ // Export Durable Object class for Cloudflare Workers binding
328
+ export { ${pascalCaseName}MCP };
329
+ `;
330
+ }
331
+ function generateAgentTs(projectName, projectNameUpper, pascalCaseName) {
332
+ return `import { MCPICloudflareAgent, type CloudflareEnv } from "@kya-os/mcp-i-cloudflare";
333
+ import { getRuntimeConfig, getTools } from "./mcpi-runtime-config";
334
+ import type { CloudflareRuntimeConfig } from "@kya-os/mcp-i-cloudflare";
335
+
336
+ /**
337
+ * MCP-I Agent Implementation
338
+ */
339
+ export class ${pascalCaseName}MCP extends MCPICloudflareAgent {
340
+ protected getAgentName(): string {
341
+ return "${projectName}";
342
+ }
343
+
344
+ protected getAgentVersion(): string {
345
+ return "1.0.0";
346
+ }
347
+
348
+ protected getEnvPrefix(): string | undefined {
349
+ return "${projectNameUpper}";
350
+ }
351
+
352
+ protected getRuntimeConfigInternal(env: CloudflareEnv): CloudflareRuntimeConfig {
353
+ return getRuntimeConfig(env);
354
+ }
355
+
356
+ protected async registerTools(): Promise<void> {
357
+ const tools = getTools();
358
+ for (const toolDef of tools) {
359
+ this.registerToolWithProof(
360
+ toolDef.name,
361
+ toolDef.description,
362
+ toolDef.inputSchema,
363
+ toolDef.handler
364
+ );
365
+ }
366
+ }
367
+ }
368
+ `;
369
+ }
370
+ function generateRuntimeConfigTs(template) {
371
+ const imports = [`import { greetTool } from "./tools/greet";`];
372
+ const tools = ["greetTool"];
373
+ if (template === "ecommerce") {
374
+ imports.push(`import { addToCartTool } from "./tools/add-to-cart";`);
375
+ imports.push(`import { getCartTool } from "./tools/get-cart";`);
376
+ imports.push(`import { checkoutTool } from "./tools/checkout";`);
377
+ tools.push("addToCartTool", "getCartTool", "checkoutTool");
378
+ }
379
+ else if (template === "hardware-world") {
380
+ imports.push(`import { addToCartTool } from "./tools/add-to-cart";`);
381
+ imports.push(`import { getCartTool } from "./tools/get-cart";`);
382
+ imports.push(`import { removeFromCartTool } from "./tools/remove-from-cart";`);
383
+ imports.push(`import { searchProductsTool } from "./tools/search-products";`);
384
+ tools.push("addToCartTool", "getCartTool", "removeFromCartTool", "searchProductsTool");
385
+ }
386
+ return `import { defineConfig, type CloudflareRuntimeConfig } from "@kya-os/mcp-i-cloudflare";
387
+ import type { CloudflareEnv } from "@kya-os/mcp-i-cloudflare";
388
+ ${imports.join("\n")}
389
+
390
+ export function getRuntimeConfig(env: CloudflareEnv): CloudflareRuntimeConfig {
391
+ const environment = (env.MCPI_ENV || env.ENVIRONMENT || "development") as "development" | "production";
392
+
393
+ const proofingConfig = env.AGENTSHIELD_API_KEY
394
+ ? {
395
+ enabled: true,
396
+ batchQueue: {
397
+ destinations: [
398
+ {
399
+ type: "agentshield" as const,
400
+ apiKey: env.AGENTSHIELD_API_KEY,
401
+ apiUrl: env.AGENTSHIELD_API_URL || "https://kya.vouched.id",
402
+ },
403
+ ],
404
+ },
405
+ }
406
+ : undefined;
407
+
408
+ return defineConfig({
409
+ environment,
410
+ proofing: proofingConfig,
411
+ vars: {
412
+ ENVIRONMENT: environment,
413
+ AGENTSHIELD_API_KEY: env.AGENTSHIELD_API_KEY,
414
+ AGENTSHIELD_API_URL: env.AGENTSHIELD_API_URL,
415
+ AGENTSHIELD_PROJECT_ID: env.AGENTSHIELD_PROJECT_ID,
416
+ },
417
+ });
418
+ }
419
+
420
+ export function getTools() {
421
+ return [
422
+ ${tools.join(",\n ")},
423
+ ];
424
+ }
425
+ `;
426
+ }
427
+ function generateGreetToolTs(projectName) {
428
+ return `import type { ToolDefinition } from "@kya-os/mcp-i-cloudflare";
429
+
430
+ export const greetTool: ToolDefinition = {
431
+ name: "greet",
432
+ description: "Greet the user",
433
+ inputSchema: {
434
+ type: "object",
435
+ properties: {
436
+ name: { type: "string", description: "Name of the person to greet" },
437
+ },
438
+ required: ["name"],
439
+ },
440
+ handler: async (args: { name: string }) => {
441
+ return {
442
+ content: [
443
+ {
444
+ type: "text",
445
+ text: \`Hello, \${args.name}! Welcome to ${projectName}.\`,
446
+ },
447
+ ],
448
+ };
449
+ },
450
+ };
451
+ `;
452
+ }
453
+ function generateEcommerceTools() {
454
+ return [
455
+ {
456
+ path: "src/tools/add-to-cart.ts",
457
+ content: `import type { ToolDefinition } from "@kya-os/mcp-i-cloudflare";
458
+
459
+ export const addToCartTool: ToolDefinition = {
460
+ name: "add_to_cart",
461
+ description: "Add an item to the shopping cart",
462
+ inputSchema: {
463
+ type: "object",
464
+ properties: {
465
+ productId: { type: "string", description: "Product ID to add" },
466
+ quantity: { type: "number", description: "Quantity to add", default: 1 },
467
+ },
468
+ required: ["productId"],
469
+ },
470
+ handler: async (args: { productId: string; quantity?: number }) => {
471
+ const quantity = args.quantity || 1;
472
+ return {
473
+ content: [
474
+ {
475
+ type: "text",
476
+ text: \`Added \${quantity}x product \${args.productId} to cart.\`,
477
+ },
478
+ ],
479
+ };
480
+ },
481
+ };
482
+ `,
483
+ encoding: "utf-8",
484
+ },
485
+ {
486
+ path: "src/tools/get-cart.ts",
487
+ content: `import type { ToolDefinition } from "@kya-os/mcp-i-cloudflare";
488
+
489
+ export const getCartTool: ToolDefinition = {
490
+ name: "get_cart",
491
+ description: "Get the current shopping cart contents",
492
+ inputSchema: {
493
+ type: "object",
494
+ properties: {},
495
+ },
496
+ handler: async () => {
497
+ return {
498
+ content: [
499
+ {
500
+ type: "text",
501
+ text: "Your cart is currently empty. Add some items!",
502
+ },
503
+ ],
504
+ };
505
+ },
506
+ };
507
+ `,
508
+ encoding: "utf-8",
509
+ },
510
+ {
511
+ path: "src/tools/checkout.ts",
512
+ content: `import type { ToolDefinition } from "@kya-os/mcp-i-cloudflare";
513
+
514
+ export const checkoutTool: ToolDefinition = {
515
+ name: "checkout",
516
+ description: "Proceed to checkout with the current cart",
517
+ inputSchema: {
518
+ type: "object",
519
+ properties: {
520
+ paymentMethod: {
521
+ type: "string",
522
+ description: "Payment method (credit_card, paypal, etc.)",
523
+ },
524
+ },
525
+ required: ["paymentMethod"],
526
+ },
527
+ handler: async (args: { paymentMethod: string }) => {
528
+ return {
529
+ content: [
530
+ {
531
+ type: "text",
532
+ text: \`Checkout initiated with \${args.paymentMethod}. Redirecting to payment...\`,
533
+ },
534
+ ],
535
+ };
536
+ },
537
+ };
538
+ `,
539
+ encoding: "utf-8",
540
+ },
541
+ ];
542
+ }
543
+ function generateHardwareWorldTools() {
544
+ return [
545
+ {
546
+ path: "src/tools/add-to-cart.ts",
547
+ content: `import type { ToolDefinition } from "@kya-os/mcp-i-cloudflare";
548
+
549
+ export const addToCartTool: ToolDefinition = {
550
+ name: "add_to_cart",
551
+ description: "Add a hardware product to the cart",
552
+ inputSchema: {
553
+ type: "object",
554
+ properties: {
555
+ productId: { type: "string", description: "Hardware product ID" },
556
+ quantity: { type: "number", description: "Quantity", default: 1 },
557
+ },
558
+ required: ["productId"],
559
+ },
560
+ handler: async (args: { productId: string; quantity?: number }) => {
561
+ const quantity = args.quantity || 1;
562
+ return {
563
+ content: [
564
+ {
565
+ type: "text",
566
+ text: \`Added \${quantity}x \${args.productId} to your Hardware World cart.\`,
567
+ },
568
+ ],
569
+ };
570
+ },
571
+ };
572
+ `,
573
+ encoding: "utf-8",
574
+ },
575
+ {
576
+ path: "src/tools/get-cart.ts",
577
+ content: `import type { ToolDefinition } from "@kya-os/mcp-i-cloudflare";
578
+
579
+ export const getCartTool: ToolDefinition = {
580
+ name: "get_cart",
581
+ description: "View your Hardware World shopping cart",
582
+ inputSchema: {
583
+ type: "object",
584
+ properties: {},
585
+ },
586
+ handler: async () => {
587
+ return {
588
+ content: [
589
+ {
590
+ type: "text",
591
+ text: JSON.stringify({
592
+ items: [],
593
+ total: 0,
594
+ message: "Your Hardware World cart is empty!",
595
+ }),
596
+ },
597
+ ],
598
+ };
599
+ },
600
+ };
601
+ `,
602
+ encoding: "utf-8",
603
+ },
604
+ {
605
+ path: "src/tools/remove-from-cart.ts",
606
+ content: `import type { ToolDefinition } from "@kya-os/mcp-i-cloudflare";
607
+
608
+ export const removeFromCartTool: ToolDefinition = {
609
+ name: "remove_from_cart",
610
+ description: "Remove an item from the Hardware World cart",
611
+ inputSchema: {
612
+ type: "object",
613
+ properties: {
614
+ productId: { type: "string", description: "Product ID to remove" },
615
+ },
616
+ required: ["productId"],
617
+ },
618
+ handler: async (args: { productId: string }) => {
619
+ return {
620
+ content: [
621
+ {
622
+ type: "text",
623
+ text: \`Removed \${args.productId} from your cart.\`,
624
+ },
625
+ ],
626
+ };
627
+ },
628
+ };
629
+ `,
630
+ encoding: "utf-8",
631
+ },
632
+ {
633
+ path: "src/tools/search-products.ts",
634
+ content: `import type { ToolDefinition } from "@kya-os/mcp-i-cloudflare";
635
+
636
+ export const searchProductsTool: ToolDefinition = {
637
+ name: "search_products",
638
+ description: "Search Hardware World product catalog",
639
+ inputSchema: {
640
+ type: "object",
641
+ properties: {
642
+ query: { type: "string", description: "Search query" },
643
+ category: {
644
+ type: "string",
645
+ description: "Product category (tools, lumber, plumbing, electrical)",
646
+ },
647
+ },
648
+ required: ["query"],
649
+ },
650
+ handler: async (args: { query: string; category?: string }) => {
651
+ const categoryFilter = args.category ? \` in \${args.category}\` : "";
652
+ return {
653
+ content: [
654
+ {
655
+ type: "text",
656
+ text: JSON.stringify({
657
+ query: args.query,
658
+ category: args.category,
659
+ results: [
660
+ { id: "hw-001", name: "Power Drill", price: 79.99 },
661
+ { id: "hw-002", name: "Hammer", price: 24.99 },
662
+ { id: "hw-003", name: "Screwdriver Set", price: 34.99 },
663
+ ],
664
+ message: \`Found 3 products matching "\${args.query}"\${categoryFilter}\`,
665
+ }),
666
+ },
667
+ ],
668
+ };
669
+ },
670
+ };
671
+ `,
672
+ encoding: "utf-8",
673
+ },
674
+ ];
675
+ }
676
+ function generateDeployWorkflow() {
677
+ return `name: Deploy to Cloudflare
678
+
679
+ on:
680
+ push:
681
+ branches: [main]
682
+ workflow_dispatch:
683
+
684
+ jobs:
685
+ deploy:
686
+ runs-on: ubuntu-latest
687
+ name: Deploy
688
+ steps:
689
+ - uses: actions/checkout@v4
690
+
691
+ - name: Setup Node.js
692
+ uses: actions/setup-node@v4
693
+ with:
694
+ node-version: "20"
695
+
696
+ - name: Install dependencies
697
+ run: npm install
698
+
699
+ - name: Deploy to Cloudflare
700
+ uses: cloudflare/wrangler-action@v3
701
+ with:
702
+ apiToken: \${{ secrets.CLOUDFLARE_API_TOKEN }}
703
+ secrets: |
704
+ MCP_IDENTITY_PRIVATE_KEY
705
+ OAUTH_ENCRYPTION_SECRET
706
+ AGENTSHIELD_API_KEY
707
+ env:
708
+ MCP_IDENTITY_PRIVATE_KEY: \${{ secrets.MCP_IDENTITY_PRIVATE_KEY }}
709
+ OAUTH_ENCRYPTION_SECRET: \${{ secrets.OAUTH_ENCRYPTION_SECRET }}
710
+ AGENTSHIELD_API_KEY: \${{ secrets.AGENTSHIELD_API_KEY }}
711
+ `;
712
+ }
713
+ function generateReadme(projectName, projectId) {
714
+ const dashboardUrl = projectId
715
+ ? `https://kya.vouched.id/dashboard/bouncer/${projectId}`
716
+ : "https://kya.vouched.id/dashboard/bouncer";
717
+ return `# ${projectName}
718
+
719
+ MCP-I Agent deployed on Cloudflare Workers with AgentShield integration.
720
+
721
+ ## Quick Start
722
+
723
+ 1. **Clone this repository**
724
+ \`\`\`bash
725
+ git clone https://github.com/YOUR_USERNAME/${projectName}.git
726
+ cd ${projectName}
727
+ \`\`\`
728
+
729
+ 2. **Install dependencies**
730
+ \`\`\`bash
731
+ npm install
732
+ \`\`\`
733
+
734
+ 3. **Local Development**
735
+ \`\`\`bash
736
+ # Copy secrets template
737
+ cp .dev.vars.example .dev.vars
738
+
739
+ # Add your secrets to .dev.vars
740
+
741
+ # Start dev server
742
+ npm run dev
743
+ \`\`\`
744
+
745
+ 4. **Deploy**
746
+ \`\`\`bash
747
+ npm run deploy
748
+ \`\`\`
749
+
750
+ ## AgentShield Dashboard
751
+
752
+ Configure your agent at: ${dashboardUrl}
753
+
754
+ - Add tool protections
755
+ - Configure OAuth providers
756
+ - View delegations and proofs
757
+
758
+ ## Environment Variables
759
+
760
+ | Variable | Description | Required |
761
+ |----------|-------------|----------|
762
+ | \`MCP_IDENTITY_PRIVATE_KEY\` | Agent's private key | Yes |
763
+ | \`OAUTH_ENCRYPTION_SECRET\` | OAuth state encryption | Yes |
764
+ | \`AGENTSHIELD_API_KEY\` | AgentShield API key | Yes |
765
+ | \`CLOUDFLARE_API_TOKEN\` | Wrangler deployment token | For CI/CD |
766
+
767
+ ## Adding Tools
768
+
769
+ Add new tools in \`src/tools/\`:
770
+
771
+ \`\`\`typescript
772
+ // src/tools/my-tool.ts
773
+ import type { ToolDefinition } from "@kya-os/mcp-i-cloudflare";
774
+
775
+ export const myTool: ToolDefinition = {
776
+ name: "my_tool",
777
+ description: "Description of my tool",
778
+ inputSchema: {
779
+ type: "object",
780
+ properties: {
781
+ param1: { type: "string" },
782
+ },
783
+ required: ["param1"],
784
+ },
785
+ handler: async (args) => {
786
+ return {
787
+ content: [{ type: "text", text: \`Result: \${args.param1}\` }],
788
+ };
789
+ },
790
+ };
791
+ \`\`\`
792
+
793
+ Then register in \`src/mcpi-runtime-config.ts\`.
794
+
795
+ ## License
796
+
797
+ MIT
798
+ `;
799
+ }
800
+ //# sourceMappingURL=generate-cloudflare-files.js.map