@kya-os/create-mcpi-app 1.3.5-canary.1 → 1.3.5-canary.11

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.
@@ -6,7 +6,7 @@ import chalk from "chalk";
6
6
  * Uses McpAgent from agents/mcp for MCP protocol support
7
7
  */
8
8
  export async function fetchCloudflareMcpiTemplate(projectPath, options = {}) {
9
- const { packageManager = "npm", projectName = path.basename(projectPath) } = options;
9
+ const { packageManager = "npm", projectName = path.basename(projectPath), apikey } = options;
10
10
  // Sanitize project name for class names
11
11
  const className = projectName
12
12
  .replace(/[^a-zA-Z0-9]/g, "")
@@ -23,16 +23,31 @@ export async function fetchCloudflareMcpiTemplate(projectPath, options = {}) {
23
23
  deploy: "wrangler deploy",
24
24
  dev: "wrangler dev",
25
25
  start: "wrangler dev",
26
- "kv:create": "npm run kv:create-nonce && npm run kv:create-proof",
27
- "kv:create-nonce": `wrangler kv namespace create NONCE_CACHE`,
28
- "kv:create-proof": `wrangler kv namespace create PROOF_ARCHIVE`,
29
- "kv:list": "wrangler kv namespace list | grep -E '(NONCE|PROOF|MCPI)' || wrangler kv namespace list",
30
- "kv:setup": "echo 'Run npm run kv:list to find existing namespaces, or npm run kv:create to create both, or use kv:create-nonce and kv:create-proof individually'",
26
+ "kv:create": "npm run kv:create-nonce && npm run kv:create-proof && npm run kv:create-identity && npm run kv:create-delegation && npm run kv:create-tool-protection",
27
+ "kv:create-nonce": `wrangler kv namespace create ${className.toUpperCase()}_NONCE_CACHE`,
28
+ "kv:create-proof": `wrangler kv namespace create ${className.toUpperCase()}_PROOF_ARCHIVE`,
29
+ "kv:create-identity": `wrangler kv namespace create ${className.toUpperCase()}_IDENTITY_STORAGE`,
30
+ "kv:create-delegation": `wrangler kv namespace create ${className.toUpperCase()}_DELEGATION_STORAGE`,
31
+ "kv:create-tool-protection": `wrangler kv namespace create ${className.toUpperCase()}_TOOL_PROTECTION_KV`,
32
+ "kv:list": "wrangler kv namespace list | grep -E '(NONCE|PROOF|IDENTITY|DELEGATION|TOOL_PROTECTION|MCPI)' || wrangler kv namespace list",
33
+ "kv:keys-nonce": `wrangler kv key list --binding=${className.toUpperCase()}_NONCE_CACHE`,
34
+ "kv:keys-proof": `wrangler kv key list --binding=${className.toUpperCase()}_PROOF_ARCHIVE`,
35
+ "kv:keys-identity": `wrangler kv key list --binding=${className.toUpperCase()}_IDENTITY_STORAGE`,
36
+ "kv:keys-delegation": `wrangler kv key list --binding=${className.toUpperCase()}_DELEGATION_STORAGE`,
37
+ "kv:keys-tool-protection": `wrangler kv key list --binding=${className.toUpperCase()}_TOOL_PROTECTION_KV`,
38
+ "kv:delete-nonce": `wrangler kv namespace delete --binding=${className.toUpperCase()}_NONCE_CACHE`,
39
+ "kv:delete-proof": `wrangler kv namespace delete --binding=${className.toUpperCase()}_PROOF_ARCHIVE`,
40
+ "kv:delete-identity": `wrangler kv namespace delete --binding=${className.toUpperCase()}_IDENTITY_STORAGE`,
41
+ "kv:delete-delegation": `wrangler kv namespace delete --binding=${className.toUpperCase()}_DELEGATION_STORAGE`,
42
+ "kv:delete-tool-protection": `wrangler kv namespace delete --binding=${className.toUpperCase()}_TOOL_PROTECTION_KV`,
43
+ "kv:delete": "npm run kv:delete-nonce && npm run kv:delete-proof && npm run kv:delete-identity && npm run kv:delete-delegation && npm run kv:delete-tool-protection",
44
+ "kv:reset": "npm run kv:delete && npm run kv:create",
45
+ "kv:setup": "echo 'KV Commands: kv:create (create all), kv:list (list all), kv:keys-* (view keys), kv:delete (delete all), kv:reset (delete+recreate)'",
31
46
  "cf-typegen": "wrangler types",
32
47
  "type-check": "tsc --noEmit",
33
48
  },
34
49
  dependencies: {
35
- "@kya-os/mcp-i-cloudflare": "^1.1.1",
50
+ "@kya-os/mcp-i-cloudflare": "1.2.3-canary.8",
36
51
  "@modelcontextprotocol/sdk": "^1.19.1",
37
52
  "agents": "^0.2.8",
38
53
  "hono": "^4.9.10",
@@ -53,6 +68,31 @@ export async function fetchCloudflareMcpiTemplate(projectPath, options = {}) {
53
68
  // Create greet tool
54
69
  const greetToolContent = `import { z } from "zod";
55
70
 
71
+ /**
72
+ * Greet Tool - Example MCP tool with AgentShield integration
73
+ *
74
+ * This tool demonstrates proper scopeId configuration for tool auto-discovery.
75
+ *
76
+ * Configure the corresponding scope in mcpi-runtime-config.ts:
77
+ * \`\`\`typescript
78
+ * toolProtections: {
79
+ * greet: {
80
+ * requiresDelegation: false,
81
+ * requiredScopes: ["greet:execute"], // ← This becomes the scopeId in proofs
82
+ * }
83
+ * }
84
+ * \`\`\`
85
+ *
86
+ * The scopeId format is "toolName:action":
87
+ * - Tool name: "greet" (extracted before the ":")
88
+ * - Action: "execute" (extracted after the ":")
89
+ * - Risk level: Auto-determined from action keyword (execute = high)
90
+ *
91
+ * Other scopeId examples:
92
+ * - "files:read" → Medium risk
93
+ * - "files:write" → High risk
94
+ * - "database:delete" → Critical risk
95
+ */
56
96
  export const greetTool = {
57
97
  name: "greet",
58
98
  description: "Greet a user by name",
@@ -72,13 +112,166 @@ export const greetTool = {
72
112
  };
73
113
  `;
74
114
  fs.writeFileSync(path.join(toolsDir, "greet.ts"), greetToolContent);
115
+ // Create mcpi-runtime-config.ts for AgentShield integration
116
+ const runtimeConfigContent = `import type { MCPIRuntimeConfig } from "@kya-os/mcp-i-cloudflare";
117
+ ${apikey ? 'import { CloudflareRuntime } from "@kya-os/mcp-i-cloudflare"; // Auto-enabled: Tool Protection Service' : '// import { CloudflareRuntime } from "@kya-os/mcp-i-cloudflare"; // Uncomment to enable Tool Protection Service'}
118
+
119
+ /**
120
+ * Runtime configuration for MCP-I server
121
+ *
122
+ * This file configures runtime features like proof submission to AgentShield,
123
+ * delegation verification, and audit logging.
124
+ *
125
+ * Environment variables are automatically injected from wrangler.toml (Cloudflare)
126
+ * or .env (Node.js). Configure them there:
127
+ * - AGENTSHIELD_API_URL: AgentShield API base URL
128
+ * - AGENTSHIELD_API_KEY: Your AgentShield API key
129
+ * - AGENTSHIELD_PROJECT_ID: Your AgentShield project ID
130
+ * - MCPI_ENV: "development" or "production"
131
+ */
132
+ export function getRuntimeConfig(): MCPIRuntimeConfig {
133
+ const env = process.env;
134
+
135
+ return {
136
+ // Identity configuration
137
+ identity: {
138
+ environment: (env.MCPI_ENV as "development" | "production") || "development",
139
+ devIdentityPath: ".mcpi/identity.json"
140
+ },
141
+
142
+ // Proof submission configuration
143
+ proofing: {
144
+ enabled: true,
145
+ batchQueue: {
146
+ destinations: [
147
+ {
148
+ type: "agentshield" as const,
149
+ apiUrl: env.AGENTSHIELD_API_URL || "https://kya.vouched.id",
150
+ apiKey: env.AGENTSHIELD_API_KEY || ""
151
+ }
152
+ ],
153
+ maxBatchSize: 10,
154
+ flushIntervalMs: 5000,
155
+ maxRetries: 3,
156
+ debug: env.MCPI_ENV === "development"
157
+ }
158
+ },
159
+
160
+ // Delegation verification (AgentShield API)
161
+ delegation: {
162
+ enabled: true,
163
+ enforceDelegations: true, // ✅ NEW: Enable enforcement of delegation requirements
164
+ verifier: {
165
+ type: "agentshield-api",
166
+ agentshield: {
167
+ apiUrl: env.AGENTSHIELD_API_URL || "https://kya.vouched.id",
168
+ apiKey: env.AGENTSHIELD_API_KEY || ""
169
+ },
170
+ cacheTtl: 60000, // 1 minute cache
171
+ debug: env.MCPI_ENV === "development"
172
+ },
173
+ authorization: {
174
+ authorizationUrl: env.AUTHORIZATION_URL || \`\${env.AGENTSHIELD_API_URL}/authorize\`,
175
+ resumeTokenTtl: 600000, // 10 minutes
176
+ minReputationScore: 76
177
+ },
178
+ // ⚠️ DEPRECATED (if using dynamic tool protection):
179
+ // Tool protection rules - Configure scopes for auto-discovery in AgentShield
180
+ //
181
+ // NOTE: These are now managed via AgentShield dashboard and fetched dynamically
182
+ // when you enable the Tool Protection Service (see below). This fallback config
183
+ // is only used if:
184
+ // 1. Tool Protection Service is not configured, OR
185
+ // 2. AgentShield API is unavailable
186
+ toolProtections: {
187
+ // Example: Public tool with execution scope
188
+ greet: {
189
+ requiresDelegation: false, // No delegation needed for low-risk tool
190
+ requiredScopes: ["greet:execute"] // ✅ Enables tool discovery in AgentShield
191
+ }
192
+ // Add more tools as needed:
193
+ // High-risk tool requiring delegation:
194
+ // checkout: {
195
+ // requiresDelegation: true, // User must explicitly delegate
196
+ // requiredScopes: ["checkout:execute"] // Scope for tool discovery
197
+ // },
198
+ // delete_file: {
199
+ // requiresDelegation: true,
200
+ // requiredScopes: ["files:delete"] // Action-based scope for better categorization
201
+ // }
202
+ }
203
+ },
204
+
205
+ // 🆕 Dynamic Tool Protection Service (Dashboard-Controlled)
206
+ //
207
+ // When AgentShield API key is configured, tool delegation requirements
208
+ // are fetched from the dashboard in real-time (5-minute cache).
209
+ //
210
+ // Benefits:
211
+ // - Toggle "Require Delegation" for any tool in dashboard - no code changes
212
+ // - Changes apply in real-time (5-minute cache via KV)
213
+ // - Automatic tool discovery from proof submissions
214
+ //
215
+ // Performance: 1 API call per 5 minutes, all other requests use KV cache
216
+ ${apikey ? `toolProtectionService: env.TOOL_PROTECTION_KV
217
+ ? CloudflareRuntime.createToolProtectionService(
218
+ env.TOOL_PROTECTION_KV, // KV namespace from wrangler.toml
219
+ {
220
+ apiUrl: env.AGENTSHIELD_API_URL || "https://kya.vouched.id",
221
+ apiKey: env.AGENTSHIELD_API_KEY || "",
222
+ projectId: env.AGENTSHIELD_PROJECT_ID || "",
223
+ cacheTtl: 300000, // 5 minutes (in milliseconds)
224
+ debug: env.MCPI_ENV === "development",
225
+ // Fallback to local config if API unavailable
226
+ fallbackConfig: {
227
+ toolProtections: {
228
+ greet: {
229
+ requiresDelegation: false,
230
+ requiredScopes: ["greet:execute"],
231
+ },
232
+ },
233
+ },
234
+ }
235
+ )
236
+ : undefined,` : `// toolProtectionService: env.TOOL_PROTECTION_KV
237
+ // ? CloudflareRuntime.createToolProtectionService(
238
+ // env.TOOL_PROTECTION_KV,
239
+ // {
240
+ // apiUrl: env.AGENTSHIELD_API_URL || "https://kya.vouched.id",
241
+ // apiKey: env.AGENTSHIELD_API_KEY || "",
242
+ // projectId: env.AGENTSHIELD_PROJECT_ID || "",
243
+ // cacheTtl: 300000, // 5 minutes
244
+ // debug: env.MCPI_ENV === "development",
245
+ // fallbackConfig: {
246
+ // toolProtections: {
247
+ // greet: {
248
+ // requiresDelegation: false,
249
+ // requiredScopes: ["greet:execute"],
250
+ // },
251
+ // },
252
+ // },
253
+ // }
254
+ // )
255
+ // : undefined,`}
256
+
257
+ // Audit logging
258
+ audit: {
259
+ enabled: true
260
+ }
261
+ };
262
+ }
263
+
264
+ export default getRuntimeConfig();
265
+ `;
266
+ fs.writeFileSync(path.join(srcDir, "mcpi-runtime-config.ts"), runtimeConfigContent);
75
267
  // Create main index.ts using McpAgent with MCP-I runtime
76
268
  const indexContent = `import { McpAgent } from "agents/mcp";
77
269
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
78
- import { createCloudflareRuntime, type CloudflareEnv, KVProofArchive } from "@kya-os/mcp-i-cloudflare";
270
+ import { createCloudflareRuntime, type CloudflareEnv, KVProofArchive, type DetachedProof } from "@kya-os/mcp-i-cloudflare";
79
271
  import { Hono } from "hono";
80
272
  import { cors } from "hono/cors";
81
273
  import { greetTool } from "./tools/greet";
274
+ import { getRuntimeConfig } from "./mcpi-runtime-config";
82
275
 
83
276
  export class ${pascalClassName}MCP extends McpAgent {
84
277
  server = new McpServer({
@@ -88,27 +281,136 @@ export class ${pascalClassName}MCP extends McpAgent {
88
281
 
89
282
  private mcpiRuntime?: any;
90
283
  private proofArchive?: KVProofArchive;
284
+ private agentShieldConfig?: { apiUrl: string; apiKey: string };
91
285
  private env: CloudflareEnv;
92
286
 
93
287
  constructor(state: DurableObjectState, env: CloudflareEnv) {
94
288
  super(state, env);
95
289
  this.env = env;
96
290
 
291
+ // Load runtime configuration for AgentShield integration
292
+ const runtimeConfig = getRuntimeConfig();
293
+
294
+ // Create CloudflareEnv adapter to map prefixed KV bindings to expected names
295
+ // This allows multiple agents to be deployed without KV namespace conflicts
296
+ const mappedEnv: CloudflareEnv = {
297
+ // Map prefixed bindings to standard names expected by createCloudflareRuntime
298
+ NONCE_CACHE: (env as any).${className.toUpperCase()}_NONCE_CACHE,
299
+ PROOF_ARCHIVE: (env as any).${className.toUpperCase()}_PROOF_ARCHIVE,
300
+ IDENTITY_STORAGE: (env as any).${className.toUpperCase()}_IDENTITY_STORAGE,
301
+ TOOL_PROTECTION_KV: (env as any).${className.toUpperCase()}_TOOL_PROTECTION_KV,
302
+ // Pass through environment variables unchanged
303
+ MCP_IDENTITY_PRIVATE_KEY: (env as any).MCP_IDENTITY_PRIVATE_KEY,
304
+ MCP_IDENTITY_PUBLIC_KEY: (env as any).MCP_IDENTITY_PUBLIC_KEY,
305
+ MCP_IDENTITY_AGENT_DID: (env as any).MCP_IDENTITY_AGENT_DID,
306
+ };
307
+
97
308
  // Initialize MCP-I runtime for cryptographic proofs and identity
98
309
  this.mcpiRuntime = createCloudflareRuntime({
99
- env: env,
310
+ env: mappedEnv,
100
311
  audit: {
101
- enabled: true,
102
- logFunction: (record) => console.log('[MCP-I Audit]', record)
312
+ enabled: runtimeConfig.audit?.enabled ?? true,
313
+ logFunction: runtimeConfig.audit?.logFunction || ((record) => console.log('[MCP-I Audit]', record))
103
314
  }
104
315
  });
105
316
 
106
317
  // Initialize proof archive if PROOF_ARCHIVE KV is available
107
- if (env.PROOF_ARCHIVE) {
108
- this.proofArchive = new KVProofArchive(env.PROOF_ARCHIVE);
109
- console.log('[MCP-I] Proof archive enabled - proofs will be stored in KV');
110
- } else {
111
- console.warn('[MCP-I] PROOF_ARCHIVE KV not configured - proofs will only be logged');
318
+ if (mappedEnv.PROOF_ARCHIVE) {
319
+ this.proofArchive = new KVProofArchive(mappedEnv.PROOF_ARCHIVE);
320
+ console.log('[MCP-I] Proof archive enabled');
321
+ }
322
+
323
+ // Load AgentShield config for proof submission
324
+ if (runtimeConfig.proofing?.enabled && runtimeConfig.proofing.batchQueue) {
325
+ const agentShieldDest = runtimeConfig.proofing.batchQueue.destinations?.find(
326
+ (dest) => dest.type === "agentshield" && dest.apiKey
327
+ );
328
+ if (agentShieldDest) {
329
+ this.agentShieldConfig = {
330
+ apiUrl: agentShieldDest.apiUrl,
331
+ apiKey: agentShieldDest.apiKey!
332
+ };
333
+ console.log('[MCP-I] AgentShield enabled:', agentShieldDest.apiUrl);
334
+ }
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Submit proof to AgentShield API
340
+ * Uses the proof.jws directly (full JWS format from CloudflareRuntime)
341
+ *
342
+ * Also submits optional context for AgentShield dashboard integration.
343
+ * Context provides plaintext tool/args data while proof provides cryptographic verification.
344
+ */
345
+ private async submitProofToAgentShield(
346
+ proof: DetachedProof,
347
+ session: any,
348
+ toolName: string,
349
+ args: any,
350
+ result: any
351
+ ): Promise<void> {
352
+ if (!this.agentShieldConfig || !proof.jws || !proof.meta) return;
353
+
354
+ const { apiUrl, apiKey } = this.agentShieldConfig;
355
+
356
+ // Get tool call context from runtime (if available)
357
+ const toolCallContext = this.mcpiRuntime?.getLastToolCallContext();
358
+
359
+ // Proof already has correct format from CloudflareRuntime
360
+ // Adding optional context for AgentShield dashboard (Option A architecture)
361
+ const requestBody = {
362
+ session_id: session.id,
363
+ delegation_id: null,
364
+ proofs: [{
365
+ jws: proof.jws, // Already in full JWS format
366
+ meta: proof.meta // Already has all required fields
367
+ }],
368
+ // ✅ NEW: Optional context for dashboard integration
369
+ context: {
370
+ toolCalls: toolCallContext ? [toolCallContext] : [{
371
+ // Fallback if context not available from runtime
372
+ tool: toolName,
373
+ args: args,
374
+ result: result,
375
+ scopeId: proof.meta.scopeId || \`\${toolName}:execute\`
376
+ }],
377
+ // ✅ NEW: MCP server URL for tool discovery (optional, only needed once)
378
+ mcpServerUrl: env.MCP_SERVER_URL
379
+ }
380
+ };
381
+
382
+ console.log('[AgentShield] Submitting proof with context:', {
383
+ did: proof.meta.did,
384
+ sessionId: proof.meta.sessionId,
385
+ jwsFormat: proof.jws.split('.').length === 3 ? 'valid (3 parts)' : 'invalid',
386
+ contextTool: requestBody.context.toolCalls[0]?.tool,
387
+ contextScopeId: requestBody.context.toolCalls[0]?.scopeId,
388
+ mcpServerUrl: requestBody.context.mcpServerUrl || 'not-set'
389
+ });
390
+
391
+ const response = await fetch(\`\${apiUrl}/api/v1/bouncer/proofs\`, {
392
+ method: 'POST',
393
+ headers: {
394
+ 'Content-Type': 'application/json',
395
+ 'Authorization': \`Bearer \${apiKey}\`
396
+ },
397
+ body: JSON.stringify(requestBody)
398
+ });
399
+
400
+ if (!response.ok) {
401
+ const errorText = await response.text();
402
+ console.error('[AgentShield] Submission failed:', response.status, errorText);
403
+ throw new Error(\`AgentShield error: \${response.status}\`);
404
+ }
405
+
406
+ const responseData = await response.json() as any;
407
+ console.log('[AgentShield] Response:', responseData);
408
+
409
+ if (responseData.accepted) {
410
+ console.log('[AgentShield] ✅ Proofs accepted:', responseData.accepted);
411
+ }
412
+ if (responseData.rejected) {
413
+ console.log('[AgentShield] ❌ Proofs rejected:', responseData.rejected);
112
414
  }
113
415
  }
114
416
 
@@ -131,7 +433,7 @@ export class ${pascalClassName}MCP extends McpAgent {
131
433
  const timestamp = Date.now();
132
434
  const session = {
133
435
  id: \`ephemeral-\${timestamp}-\${Math.random().toString(36).substring(2, 10)}\`,
134
- audience: 'mcp-client',
436
+ audience: 'https://kya.vouched.id', // CRITICAL: Must match AgentShield domain
135
437
  agentDid: (await this.mcpiRuntime.getIdentity()).did,
136
438
  createdAt: timestamp,
137
439
  expiresAt: timestamp + (30 * 60 * 1000) // 30 minutes
@@ -145,41 +447,52 @@ export class ${pascalClassName}MCP extends McpAgent {
145
447
  session
146
448
  );
147
449
 
148
- // Get the proof that was just generated
149
- const proof = this.mcpiRuntime.getLastProof();
450
+ // Get proof in DetachedProof format
451
+ const proof = this.mcpiRuntime.getLastProof() as DetachedProof;
150
452
 
151
- if (proof) {
453
+ if (proof && proof.jws && proof.meta) {
454
+ // Log proof details (using DetachedProof format)
152
455
  console.log('[MCP-I Proof]', {
153
456
  tool: greetTool.name,
154
- did: proof.did,
155
- timestamp: proof.timestamp,
156
- signature: proof.signature.substring(0, 20) + '...'
457
+ did: proof.meta.did,
458
+ timestamp: proof.meta.ts,
459
+ jws: proof.jws.substring(0, 50) + '...',
460
+ jwsValid: proof.jws.split('.').length === 3
157
461
  });
158
462
 
159
- // Store proof in KV archive (if configured)
463
+ // Store in KV archive
160
464
  if (this.proofArchive) {
161
465
  try {
162
466
  await this.proofArchive.store(proof, {
163
- toolName: greetTool.name,
164
- sessionId: session.id
467
+ toolName: greetTool.name
165
468
  });
166
- console.log('[MCP-I] Proof stored in PROOF_ARCHIVE KV');
469
+ console.log('[MCP-I] Proof stored in archive');
167
470
  } catch (archiveError) {
168
- console.error('[MCP-I] Failed to store proof in archive:', archiveError);
169
- // Continue even if archiving fails
471
+ console.error('[MCP-I] Archive error:', archiveError);
472
+ }
473
+ }
474
+
475
+ // Submit to AgentShield with context
476
+ if (this.agentShieldConfig) {
477
+ try {
478
+ await this.submitProofToAgentShield(proof, session, greetTool.name, args, result);
479
+ } catch (err: any) {
480
+ console.error('[MCP-I] AgentShield failed:', err.message);
170
481
  }
171
482
  }
172
483
 
173
- // Attach proof to result for MCP Inspector and clients
174
- // Following MCP-I pattern: { content: [...], _meta: { proof: {...} } }
484
+ // Attach proof to result for MCP Inspector
175
485
  if (result && typeof result === 'object') {
176
486
  (result as any)._meta = {
177
487
  proof: {
178
- did: proof.did,
179
- signature: proof.signature,
180
- timestamp: proof.timestamp,
181
- nonce: proof.nonce,
182
- algorithm: proof.algorithm
488
+ jws: proof.jws,
489
+ did: proof.meta.did,
490
+ kid: proof.meta.kid,
491
+ timestamp: proof.meta.ts,
492
+ nonce: proof.meta.nonce,
493
+ sessionId: proof.meta.sessionId,
494
+ requestHash: proof.meta.requestHash,
495
+ responseHash: proof.meta.responseHash
183
496
  }
184
497
  };
185
498
  }
@@ -221,7 +534,7 @@ app.mount("/mcp", ${pascalClassName}MCP.serve("/mcp").fetch, { replaceRequest: f
221
534
  export default app;
222
535
  `;
223
536
  fs.writeFileSync(path.join(srcDir, "index.ts"), indexContent);
224
- // Create wrangler.toml
537
+ // Create wrangler.toml with optional API key
225
538
  const wranglerContent = `#:schema node_modules/wrangler/config-schema.json
226
539
  name = "${projectName}"
227
540
  main = "src/index.ts"
@@ -246,8 +559,8 @@ new_sqlite_classes = ["${pascalClassName}MCP"]
246
559
  #
247
560
  # Then replace the id below with the namespace ID from the output
248
561
  [[kv_namespaces]]
249
- binding = "NONCE_CACHE" # Binding name must match runtime expectation
250
- id = "your-nonce-kv-namespace-id" # Replace with actual namespace ID
562
+ binding = "${className.toUpperCase()}_NONCE_CACHE"
563
+ id = "your_nonce_kv_namespace_id" # Replace with actual namespace ID
251
564
 
252
565
  # KV Namespace for proof archive (RECOMMENDED for auditability)
253
566
  #
@@ -259,12 +572,67 @@ id = "your-nonce-kv-namespace-id" # Replace with actual namespace ID
259
572
  #
260
573
  # Note: Comment out if you don't need proof archiving
261
574
  [[kv_namespaces]]
262
- binding = "PROOF_ARCHIVE" # Binding name must match runtime expectation
263
- id = "your-proof-kv-namespace-id" # Replace with actual namespace ID
575
+ binding = "${className.toUpperCase()}_PROOF_ARCHIVE"
576
+ id = "your_proof_kv_namespace_id" # Replace with actual namespace ID
577
+
578
+ # KV Namespace for identity storage (RECOMMENDED for persistent agent identity)
579
+ #
580
+ # Stores the agent's cryptographic identity (DID, keys) in KV
581
+ # Ensures consistent identity across Worker restarts and deployments
582
+ #
583
+ # Run: npm run kv:create-identity (creates IDENTITY_STORAGE namespace)
584
+ # Then replace the id below with the namespace ID from the output
585
+ #
586
+ [[kv_namespaces]]
587
+ binding = "${className.toUpperCase()}_IDENTITY_STORAGE"
588
+ id = "your_identity_kv_namespace_id" # Replace with actual namespace ID
589
+
590
+ # KV Namespace for delegation storage (REQUIRED for OAuth/delegation flows)
591
+ #
592
+ # Stores active delegations from users to agents
593
+ # Enables OAuth consent flows and scope-based authorization
594
+ #
595
+ # Run: npm run kv:create-delegation (creates DELEGATION_STORAGE namespace)
596
+ # Then replace the id below with the namespace ID from the output
597
+ #
598
+ [[kv_namespaces]]
599
+ binding = "${className.toUpperCase()}_DELEGATION_STORAGE"
600
+ id = "your_delegation_kv_namespace_id" # Replace with actual namespace ID
601
+
602
+ # KV Namespace for tool protection config (${apikey ? 'ENABLED' : 'OPTIONAL'} for dashboard-controlled delegation)
603
+ #
604
+ # 🆕 Enables dynamic tool protection configuration from AgentShield dashboard
605
+ # Caches which tools require user delegation based on dashboard toggle switches
606
+ #
607
+ # Benefits:
608
+ # - Control tool permissions from AgentShield dashboard without code changes
609
+ # - Update delegation requirements in real-time (5-minute cache)
610
+ # - No redeployments needed to change tool permissions
611
+ #
612
+ # Setup:
613
+ # 1. Run: npm run kv:create-tool-protection
614
+ # 2. Copy the namespace ID from output
615
+ # 3. Replace "your_tool_protection_kv_id" below with the actual ID
616
+ # 4. Deploy and toggle delegation in AgentShield dashboard
617
+ #
618
+ ${apikey ? '' : '# '}[[kv_namespaces]]
619
+ ${apikey ? '' : '# '}binding = "${className.toUpperCase()}_TOOL_PROTECTION_KV"
620
+ ${apikey ? '' : '# '}id = "your_tool_protection_kv_id" # Replace with actual namespace ID
264
621
 
265
622
  [vars]
266
623
  XMCP_I_TS_SKEW_SEC = "120"
267
624
  XMCP_I_SESSION_TTL = "1800"
625
+
626
+ # AgentShield Integration (https://kya.vouched.id)
627
+ # ${apikey ? 'Configure' : 'Uncomment and configure'} these variables to enable proof submission to AgentShield
628
+ AGENTSHIELD_API_URL = "https://kya.vouched.id"
629
+ ${apikey ? `AGENTSHIELD_API_KEY = "${apikey}" # Provided via --apikey flag` : '# AGENTSHIELD_API_KEY = "sk_your_api_key_here" # Get from https://kya.vouched.id/dashboard'}
630
+ ${apikey ? 'AGENTSHIELD_PROJECT_ID = "your-project-id" # Replace with your project ID from dashboard' : '# AGENTSHIELD_PROJECT_ID = "your-project-id" # Get from https://kya.vouched.id/dashboard'}
631
+ MCPI_ENV = "development"
632
+
633
+ # Optional: MCP Server URL for tool discovery
634
+ # Uncomment to explicitly set your MCP server URL (auto-detected if not set)
635
+ # MCP_SERVER_URL = "https://your-worker.workers.dev/mcp"
268
636
  `;
269
637
  fs.writeFileSync(path.join(projectPath, "wrangler.toml"), wranglerContent);
270
638
  // Create tsconfig.json
@@ -319,37 +687,49 @@ ${packageManager} install
319
687
 
320
688
  ### 2. Create KV Namespaces
321
689
 
322
- #### Create Nonce Cache (Required)
690
+ #### Create All KV Namespaces (Recommended)
323
691
 
324
692
  \`\`\`bash
325
- ${packageManager === "npm" ? "npm run" : packageManager} kv:create-nonce
693
+ ${packageManager === "npm" ? "npm run" : packageManager} kv:create
326
694
  \`\`\`
327
695
 
328
- Copy the \`id\` from the output and update \`wrangler.toml\`:
696
+ This creates all 5 KV namespaces at once:
697
+ - \`NONCE_CACHE\` - Replay attack prevention (Required)
698
+ - \`PROOF_ARCHIVE\` - Cryptographic proof storage (Recommended)
699
+ - \`IDENTITY_STORAGE\` - Agent identity persistence (Recommended)
700
+ - \`DELEGATION_STORAGE\` - OAuth delegation storage (Required for delegation)
701
+ - \`TOOL_PROTECTION_KV\` - Dashboard-controlled permissions (Optional)
702
+
703
+ Copy the namespace IDs from the output and update each one in \`wrangler.toml\`:
329
704
 
330
705
  \`\`\`toml
331
706
  [[kv_namespaces]]
332
707
  binding = "NONCE_CACHE"
333
- id = "your-nonce-kv-id-here" # ← Update this
334
- \`\`\`
335
-
336
- #### Create Proof Archive (Recommended)
708
+ id = "your_nonce_kv_id_here" # ← Update this
337
709
 
338
- \`\`\`bash
339
- ${packageManager === "npm" ? "npm run" : packageManager} kv:create-proof
340
- \`\`\`
710
+ [[kv_namespaces]]
711
+ binding = "PROOF_ARCHIVE"
712
+ id = "your_proof_kv_id_here" # ← Update this
341
713
 
342
- This runs: \`wrangler kv namespace create PROOF_ARCHIVE\`
714
+ [[kv_namespaces]]
715
+ binding = "IDENTITY_STORAGE"
716
+ id = "your_identity_kv_id_here" # ← Update this
343
717
 
344
- Copy the \`id\` from the output and update \`wrangler.toml\`:
718
+ [[kv_namespaces]]
719
+ binding = "DELEGATION_STORAGE"
720
+ id = "your_delegation_kv_id_here" # ← Update this
345
721
 
346
- \`\`\`toml
347
722
  [[kv_namespaces]]
348
- binding = "PROOF_ARCHIVE"
349
- id = "your-proof-kv-id-here" # ← Update this
723
+ binding = "TOOL_PROTECTION_KV"
724
+ id = "your_tool_protection_kv_id_here" # ← Update this
350
725
  \`\`\`
351
726
 
352
- **Note:** The PROOF_ARCHIVE stores cryptographic proofs for auditability. If you don't need proof archiving, you can comment out this namespace in \`wrangler.toml\`.
727
+ **Note:** You can also create namespaces individually:
728
+ - \`${packageManager === "npm" ? "npm run" : packageManager} kv:create-nonce\` - Create nonce cache only
729
+ - \`${packageManager === "npm" ? "npm run" : packageManager} kv:create-proof\` - Create proof archive only
730
+ - \`${packageManager === "npm" ? "npm run" : packageManager} kv:create-identity\` - Create identity storage only
731
+ - \`${packageManager === "npm" ? "npm run" : packageManager} kv:create-delegation\` - Create delegation storage only
732
+ - \`${packageManager === "npm" ? "npm run" : packageManager} kv:create-tool-protection\` - Create tool protection cache only
353
733
 
354
734
  ### 3. Test Locally
355
735
 
@@ -444,10 +824,10 @@ If you configured the \`PROOF_ARCHIVE\` KV namespace, proofs are also stored for
444
824
 
445
825
  \`\`\`bash
446
826
  # List all proofs
447
- wrangler kv:key list --namespace-id=your-proof-kv-id
827
+ wrangler kv:key list --namespace-id=your_proof_kv_id
448
828
 
449
829
  # View a specific proof
450
- wrangler kv:key get "proof_1234567890_abcd" --namespace-id=your-proof-kv-id
830
+ wrangler kv:key get "proof_1234567890_abcd" --namespace-id=your_proof_kv_id
451
831
  \`\`\`
452
832
 
453
833
  ## Identity Management
@@ -462,6 +842,105 @@ The identity includes:
462
842
  - \`publicKey\`: Ed25519 public key for signature verification
463
843
  - \`privateKey\`: Ed25519 private key (secured in Durable Object state)
464
844
 
845
+ ## AgentShield Integration
846
+
847
+ This project is configured to send cryptographic proofs to AgentShield for audit trails and compliance monitoring.
848
+
849
+ ### Setup
850
+
851
+ 1. **Get your AgentShield API key**:
852
+ - Sign up at https://kya.vouched.id
853
+ - Create a project
854
+ - Copy your API key from the dashboard
855
+
856
+ 2. **Update \`wrangler.toml\`**:
857
+ \`\`\`toml
858
+ [vars]
859
+ AGENTSHIELD_API_URL = "https://kya.vouched.id"
860
+ AGENTSHIELD_API_KEY = "sk_your_actual_key_here" # ← Replace this
861
+ AGENTSHIELD_PROJECT_ID = "your-project-id" # ← Replace this
862
+ MCPI_ENV = "development"
863
+ \`\`\`
864
+
865
+ 3. **Test proof submission**:
866
+ \`\`\`bash
867
+ ${packageManager === "npm" ? "npm run" : packageManager} dev
868
+ \`\`\`
869
+
870
+ Call a tool and check the logs:
871
+ \`\`\`
872
+ [AgentShield] Submitting proof: { did: 'did:web:...', sessionId: '...', jwsFormat: 'valid (3 parts)' }
873
+ [AgentShield] ✅ Proofs accepted: 1
874
+ \`\`\`
875
+
876
+ 4. **View proofs in dashboard**:
877
+ - Go to https://kya.vouched.id/dashboard
878
+ - Select your project
879
+ - Click "Interactions" tab
880
+ - See your proofs in real-time
881
+
882
+ ### Configuration
883
+
884
+ The AgentShield integration is configured in \`src/mcpi-runtime-config.ts\`. You can customize:
885
+ - Proof batch size (\`maxBatchSize\`)
886
+ - Flush interval (\`flushIntervalMs\`)
887
+ - Retry policy (\`maxRetries\`)
888
+ - Tool protection rules (\`toolProtections\`)
889
+
890
+ ### Dashboard-Controlled Tool Protection (Advanced)
891
+
892
+ 🆕 **NEW**: Control which tools require user delegation directly from the AgentShield dashboard - no code changes needed!
893
+
894
+ Instead of hardcoding \`requiresDelegation\` in your config, enable dynamic tool protection:
895
+
896
+ 1. **Create Tool Protection KV namespace**:
897
+ \`\`\`bash
898
+ ${packageManager === "npm" ? "npm run" : packageManager} kv:create-tool-protection
899
+ \`\`\`
900
+
901
+ 2. **Uncomment TOOL_PROTECTION_KV in \`wrangler.toml\`**:
902
+ \`\`\`toml
903
+ [[kv_namespaces]]
904
+ binding = "TOOL_PROTECTION_KV"
905
+ id = "your_tool_protection_kv_id" # ← Add the ID from step 1
906
+ \`\`\`
907
+
908
+ 3. **Enable Tool Protection Service in \`src/mcpi-runtime-config.ts\`**:
909
+ - Uncomment the import: \`import { CloudflareRuntime } from "@kya-os/mcp-i-cloudflare";\`
910
+ - Uncomment the \`toolProtectionService\` configuration block
911
+
912
+ 4. **Deploy and test**:
913
+ \`\`\`bash
914
+ ${packageManager === "npm" ? "npm run" : packageManager} deploy
915
+ \`\`\`
916
+
917
+ 5. **Control delegation from dashboard**:
918
+ - Go to https://kya.vouched.id/dashboard
919
+ - Select your project → "Tools" tab
920
+ - Toggle "Require Delegation" for any tool
921
+ - Changes apply in real-time (5-minute cache)
922
+
923
+ **Benefits:**
924
+ - Update tool permissions without redeploying
925
+ - Test delegation flows instantly
926
+ - Different requirements per environment (dev vs prod)
927
+ - Automatic tool discovery from proof submissions
928
+
929
+ **Note:** The first time a tool is called, it auto-discovers in the dashboard. The \`requiresDelegation\` toggle will appear after the first proof is submitted.
930
+
931
+ ### Disable AgentShield (Optional)
932
+
933
+ If you don't want to use AgentShield, edit \`src/mcpi-runtime-config.ts\`:
934
+
935
+ \`\`\`typescript
936
+ proofing: {
937
+ enabled: false, // Disable proof submission
938
+ // ...
939
+ }
940
+ \`\`\`
941
+
942
+ Or simply don't configure the \`AGENTSHIELD_API_KEY\` environment variable.
943
+
465
944
  ## References
466
945
 
467
946
  - [Cloudflare Agents MCP](https://developers.cloudflare.com/agents/model-context-protocol/)
@@ -470,6 +949,28 @@ The identity includes:
470
949
  `;
471
950
  fs.writeFileSync(path.join(projectPath, "README.md"), readmeContent);
472
951
  console.log(chalk.green("✅ Cloudflare Worker MCP server created"));
952
+ console.log();
953
+ if (apikey) {
954
+ console.log(chalk.green("🔑 AgentShield API key configured in wrangler.toml"));
955
+ console.log(chalk.dim(" Your API key has been added to the [vars] section"));
956
+ console.log(chalk.dim(" Remember to add your AGENTSHIELD_PROJECT_ID before deployment"));
957
+ console.log();
958
+ }
959
+ else {
960
+ console.log(chalk.yellow("⚠️ No AgentShield API key provided"));
961
+ console.log(chalk.dim(" Add your API key to wrangler.toml [vars] section before deployment"));
962
+ console.log(chalk.dim(" Get your key at: https://kya.vouched.id/dashboard"));
963
+ console.log();
964
+ }
965
+ console.log(chalk.bold("📦 All KV Namespaces Configured"));
966
+ console.log(chalk.dim(" - NONCE_CACHE: Replay attack prevention"));
967
+ console.log(chalk.dim(" - PROOF_ARCHIVE: Cryptographic proof storage"));
968
+ console.log(chalk.dim(" - IDENTITY_STORAGE: Agent identity persistence"));
969
+ console.log(chalk.dim(" - DELEGATION_STORAGE: OAuth delegation storage"));
970
+ console.log(chalk.dim(" - TOOL_PROTECTION_KV: Dashboard-controlled permissions"));
971
+ console.log();
972
+ console.log(chalk.cyan(" Run 'npm run kv:create' to create all namespaces"));
973
+ console.log();
473
974
  }
474
975
  catch (error) {
475
976
  console.error(chalk.red("Failed to set up Cloudflare Worker MCP server:"), error);