@kya-os/create-mcpi-app 1.3.5-canary.0 → 1.3.5-canary.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/helpers/create.d.ts +1 -0
- package/dist/helpers/create.d.ts.map +1 -1
- package/dist/helpers/create.js +2 -1
- package/dist/helpers/create.js.map +1 -1
- package/dist/helpers/fetch-cloudflare-mcpi-template.d.ts +1 -0
- package/dist/helpers/fetch-cloudflare-mcpi-template.d.ts.map +1 -1
- package/dist/helpers/fetch-cloudflare-mcpi-template.js +535 -59
- package/dist/helpers/fetch-cloudflare-mcpi-template.js.map +1 -1
- package/dist/helpers/fetch-cloudflare-template.js +1 -1
- package/dist/helpers/generate-config.js +12 -9
- package/dist/helpers/generate-config.js.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
28
|
-
"kv:create-proof": `wrangler kv namespace create
|
|
29
|
-
"kv:
|
|
30
|
-
"kv:
|
|
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": "
|
|
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,146 @@ 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
|
+
// 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
|
+
verifier: {
|
|
164
|
+
type: "agentshield-api",
|
|
165
|
+
agentshield: {
|
|
166
|
+
apiUrl: env.AGENTSHIELD_API_URL || "https://kya.vouched.id",
|
|
167
|
+
apiKey: env.AGENTSHIELD_API_KEY || ""
|
|
168
|
+
},
|
|
169
|
+
cacheTtl: 60000, // 1 minute cache
|
|
170
|
+
debug: env.MCPI_ENV === "development"
|
|
171
|
+
},
|
|
172
|
+
authorization: {
|
|
173
|
+
authorizationUrl: env.AUTHORIZATION_URL || \`\${env.AGENTSHIELD_API_URL}/authorize\`,
|
|
174
|
+
resumeTokenTtl: 600000, // 10 minutes
|
|
175
|
+
minReputationScore: 76
|
|
176
|
+
},
|
|
177
|
+
// ⚠️ DEPRECATED (if using dynamic tool protection):
|
|
178
|
+
// Tool protection rules - Configure scopes for auto-discovery in AgentShield
|
|
179
|
+
//
|
|
180
|
+
// NOTE: These are now managed via AgentShield dashboard and fetched dynamically
|
|
181
|
+
// when you enable the Tool Protection Service (see below). This fallback config
|
|
182
|
+
// is only used if:
|
|
183
|
+
// 1. Tool Protection Service is not configured, OR
|
|
184
|
+
// 2. AgentShield API is unavailable
|
|
185
|
+
toolProtections: {
|
|
186
|
+
// Example: Public tool with execution scope
|
|
187
|
+
greet: {
|
|
188
|
+
requiresDelegation: false, // No delegation needed for low-risk tool
|
|
189
|
+
requiredScopes: ["greet:execute"] // ✅ Enables tool discovery in AgentShield
|
|
190
|
+
}
|
|
191
|
+
// Add more tools as needed:
|
|
192
|
+
// High-risk tool requiring delegation:
|
|
193
|
+
// checkout: {
|
|
194
|
+
// requiresDelegation: true, // User must explicitly delegate
|
|
195
|
+
// requiredScopes: ["checkout:execute"] // Scope for tool discovery
|
|
196
|
+
// },
|
|
197
|
+
// delete_file: {
|
|
198
|
+
// requiresDelegation: true,
|
|
199
|
+
// requiredScopes: ["files:delete"] // Action-based scope for better categorization
|
|
200
|
+
// }
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
// 🆕 NEW: Dynamic Tool Protection Service (Dashboard-Controlled)
|
|
205
|
+
//
|
|
206
|
+
// Uncomment to enable dashboard-controlled tool delegation requirements:
|
|
207
|
+
// - Toggle "Require Delegation" for any tool in AgentShield dashboard
|
|
208
|
+
// - Changes apply in real-time (5-minute cache)
|
|
209
|
+
// - No code changes or redeployments needed
|
|
210
|
+
//
|
|
211
|
+
// Setup:
|
|
212
|
+
// 1. Create TOOL_PROTECTION_KV namespace: npm run kv:create-tool-protection
|
|
213
|
+
// 2. Uncomment the KV namespace in wrangler.toml
|
|
214
|
+
// 3. Uncomment the code below
|
|
215
|
+
// 4. Deploy and test by toggling delegation in the dashboard
|
|
216
|
+
//
|
|
217
|
+
// toolProtectionService: 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
|
+
|
|
237
|
+
// Audit logging
|
|
238
|
+
audit: {
|
|
239
|
+
enabled: true
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export default getRuntimeConfig();
|
|
245
|
+
`;
|
|
246
|
+
fs.writeFileSync(path.join(srcDir, "mcpi-runtime-config.ts"), runtimeConfigContent);
|
|
75
247
|
// Create main index.ts using McpAgent with MCP-I runtime
|
|
76
248
|
const indexContent = `import { McpAgent } from "agents/mcp";
|
|
77
249
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
78
|
-
import { createCloudflareRuntime, type CloudflareEnv, KVProofArchive } from "@kya-os/mcp-i-cloudflare";
|
|
250
|
+
import { createCloudflareRuntime, type CloudflareEnv, KVProofArchive, type DetachedProof } from "@kya-os/mcp-i-cloudflare";
|
|
79
251
|
import { Hono } from "hono";
|
|
80
252
|
import { cors } from "hono/cors";
|
|
81
253
|
import { greetTool } from "./tools/greet";
|
|
254
|
+
import { getRuntimeConfig } from "./mcpi-runtime-config";
|
|
82
255
|
|
|
83
256
|
export class ${pascalClassName}MCP extends McpAgent {
|
|
84
257
|
server = new McpServer({
|
|
@@ -88,27 +261,135 @@ export class ${pascalClassName}MCP extends McpAgent {
|
|
|
88
261
|
|
|
89
262
|
private mcpiRuntime?: any;
|
|
90
263
|
private proofArchive?: KVProofArchive;
|
|
264
|
+
private agentShieldConfig?: { apiUrl: string; apiKey: string };
|
|
91
265
|
private env: CloudflareEnv;
|
|
92
266
|
|
|
93
267
|
constructor(state: DurableObjectState, env: CloudflareEnv) {
|
|
94
268
|
super(state, env);
|
|
95
269
|
this.env = env;
|
|
96
270
|
|
|
271
|
+
// Load runtime configuration for AgentShield integration
|
|
272
|
+
const runtimeConfig = getRuntimeConfig();
|
|
273
|
+
|
|
274
|
+
// Create CloudflareEnv adapter to map prefixed KV bindings to expected names
|
|
275
|
+
// This allows multiple agents to be deployed without KV namespace conflicts
|
|
276
|
+
const mappedEnv: CloudflareEnv = {
|
|
277
|
+
// Map prefixed bindings to standard names expected by createCloudflareRuntime
|
|
278
|
+
NONCE_CACHE: (env as any).${className.toUpperCase()}_NONCE_CACHE,
|
|
279
|
+
PROOF_ARCHIVE: (env as any).${className.toUpperCase()}_PROOF_ARCHIVE,
|
|
280
|
+
IDENTITY_STORAGE: (env as any).${className.toUpperCase()}_IDENTITY_STORAGE,
|
|
281
|
+
TOOL_PROTECTION_KV: (env as any).${className.toUpperCase()}_TOOL_PROTECTION_KV,
|
|
282
|
+
// Pass through environment variables unchanged
|
|
283
|
+
MCP_IDENTITY_PRIVATE_KEY: (env as any).MCP_IDENTITY_PRIVATE_KEY,
|
|
284
|
+
MCP_IDENTITY_PUBLIC_KEY: (env as any).MCP_IDENTITY_PUBLIC_KEY,
|
|
285
|
+
MCP_IDENTITY_AGENT_DID: (env as any).MCP_IDENTITY_AGENT_DID,
|
|
286
|
+
};
|
|
287
|
+
|
|
97
288
|
// Initialize MCP-I runtime for cryptographic proofs and identity
|
|
98
289
|
this.mcpiRuntime = createCloudflareRuntime({
|
|
99
|
-
env:
|
|
290
|
+
env: mappedEnv,
|
|
100
291
|
audit: {
|
|
101
|
-
enabled: true,
|
|
102
|
-
logFunction: (record) => console.log('[MCP-I Audit]', record)
|
|
292
|
+
enabled: runtimeConfig.audit?.enabled ?? true,
|
|
293
|
+
logFunction: runtimeConfig.audit?.logFunction || ((record) => console.log('[MCP-I Audit]', record))
|
|
103
294
|
}
|
|
104
295
|
});
|
|
105
296
|
|
|
106
297
|
// Initialize proof archive if PROOF_ARCHIVE KV is available
|
|
107
|
-
if (
|
|
108
|
-
this.proofArchive = new KVProofArchive(
|
|
109
|
-
console.log('[MCP-I] Proof archive enabled
|
|
110
|
-
}
|
|
111
|
-
|
|
298
|
+
if (mappedEnv.PROOF_ARCHIVE) {
|
|
299
|
+
this.proofArchive = new KVProofArchive(mappedEnv.PROOF_ARCHIVE);
|
|
300
|
+
console.log('[MCP-I] Proof archive enabled');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Load AgentShield config for proof submission
|
|
304
|
+
if (runtimeConfig.proofing?.enabled && runtimeConfig.proofing.batchQueue) {
|
|
305
|
+
const agentShieldDest = runtimeConfig.proofing.batchQueue.destinations?.find(
|
|
306
|
+
(dest) => dest.type === "agentshield" && dest.apiKey
|
|
307
|
+
);
|
|
308
|
+
if (agentShieldDest) {
|
|
309
|
+
this.agentShieldConfig = {
|
|
310
|
+
apiUrl: agentShieldDest.apiUrl,
|
|
311
|
+
apiKey: agentShieldDest.apiKey!
|
|
312
|
+
};
|
|
313
|
+
console.log('[MCP-I] AgentShield enabled:', agentShieldDest.apiUrl);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Submit proof to AgentShield API
|
|
320
|
+
* Uses the proof.jws directly (full JWS format from CloudflareRuntime)
|
|
321
|
+
*
|
|
322
|
+
* Also submits optional context for AgentShield dashboard integration.
|
|
323
|
+
* Context provides plaintext tool/args data while proof provides cryptographic verification.
|
|
324
|
+
*/
|
|
325
|
+
private async submitProofToAgentShield(
|
|
326
|
+
proof: DetachedProof,
|
|
327
|
+
session: any,
|
|
328
|
+
toolName: string,
|
|
329
|
+
args: any,
|
|
330
|
+
result: any
|
|
331
|
+
): Promise<void> {
|
|
332
|
+
if (!this.agentShieldConfig || !proof.jws || !proof.meta) return;
|
|
333
|
+
|
|
334
|
+
const { apiUrl, apiKey } = this.agentShieldConfig;
|
|
335
|
+
|
|
336
|
+
// Get tool call context from runtime (if available)
|
|
337
|
+
const toolCallContext = this.mcpiRuntime?.getLastToolCallContext();
|
|
338
|
+
|
|
339
|
+
// Proof already has correct format from CloudflareRuntime
|
|
340
|
+
// Adding optional context for AgentShield dashboard (Option A architecture)
|
|
341
|
+
const requestBody = {
|
|
342
|
+
session_id: session.id,
|
|
343
|
+
delegation_id: null,
|
|
344
|
+
proofs: [{
|
|
345
|
+
jws: proof.jws, // Already in full JWS format
|
|
346
|
+
meta: proof.meta // Already has all required fields
|
|
347
|
+
}],
|
|
348
|
+
// ✅ NEW: Optional context for dashboard integration
|
|
349
|
+
context: toolCallContext ? {
|
|
350
|
+
toolCalls: [toolCallContext]
|
|
351
|
+
} : {
|
|
352
|
+
// Fallback if context not available from runtime
|
|
353
|
+
toolCalls: [{
|
|
354
|
+
tool: toolName,
|
|
355
|
+
args: args,
|
|
356
|
+
result: result,
|
|
357
|
+
scopeId: proof.meta.scopeId || \`\${toolName}:execute\`
|
|
358
|
+
}]
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
console.log('[AgentShield] Submitting proof with context:', {
|
|
363
|
+
did: proof.meta.did,
|
|
364
|
+
sessionId: proof.meta.sessionId,
|
|
365
|
+
jwsFormat: proof.jws.split('.').length === 3 ? 'valid (3 parts)' : 'invalid',
|
|
366
|
+
contextTool: requestBody.context.toolCalls[0]?.tool,
|
|
367
|
+
contextScopeId: requestBody.context.toolCalls[0]?.scopeId
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const response = await fetch(\`\${apiUrl}/api/v1/bouncer/proofs\`, {
|
|
371
|
+
method: 'POST',
|
|
372
|
+
headers: {
|
|
373
|
+
'Content-Type': 'application/json',
|
|
374
|
+
'Authorization': \`Bearer \${apiKey}\`
|
|
375
|
+
},
|
|
376
|
+
body: JSON.stringify(requestBody)
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
if (!response.ok) {
|
|
380
|
+
const errorText = await response.text();
|
|
381
|
+
console.error('[AgentShield] Submission failed:', response.status, errorText);
|
|
382
|
+
throw new Error(\`AgentShield error: \${response.status}\`);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const responseData = await response.json() as any;
|
|
386
|
+
console.log('[AgentShield] Response:', responseData);
|
|
387
|
+
|
|
388
|
+
if (responseData.accepted) {
|
|
389
|
+
console.log('[AgentShield] ✅ Proofs accepted:', responseData.accepted);
|
|
390
|
+
}
|
|
391
|
+
if (responseData.rejected) {
|
|
392
|
+
console.log('[AgentShield] ❌ Proofs rejected:', responseData.rejected);
|
|
112
393
|
}
|
|
113
394
|
}
|
|
114
395
|
|
|
@@ -131,7 +412,7 @@ export class ${pascalClassName}MCP extends McpAgent {
|
|
|
131
412
|
const timestamp = Date.now();
|
|
132
413
|
const session = {
|
|
133
414
|
id: \`ephemeral-\${timestamp}-\${Math.random().toString(36).substring(2, 10)}\`,
|
|
134
|
-
audience: '
|
|
415
|
+
audience: 'https://kya.vouched.id', // CRITICAL: Must match AgentShield domain
|
|
135
416
|
agentDid: (await this.mcpiRuntime.getIdentity()).did,
|
|
136
417
|
createdAt: timestamp,
|
|
137
418
|
expiresAt: timestamp + (30 * 60 * 1000) // 30 minutes
|
|
@@ -145,41 +426,52 @@ export class ${pascalClassName}MCP extends McpAgent {
|
|
|
145
426
|
session
|
|
146
427
|
);
|
|
147
428
|
|
|
148
|
-
// Get
|
|
149
|
-
const proof = this.mcpiRuntime.getLastProof();
|
|
429
|
+
// Get proof in DetachedProof format
|
|
430
|
+
const proof = this.mcpiRuntime.getLastProof() as DetachedProof;
|
|
150
431
|
|
|
151
|
-
if (proof) {
|
|
432
|
+
if (proof && proof.jws && proof.meta) {
|
|
433
|
+
// Log proof details (using DetachedProof format)
|
|
152
434
|
console.log('[MCP-I Proof]', {
|
|
153
435
|
tool: greetTool.name,
|
|
154
|
-
did: proof.did,
|
|
155
|
-
timestamp: proof.
|
|
156
|
-
|
|
436
|
+
did: proof.meta.did,
|
|
437
|
+
timestamp: proof.meta.ts,
|
|
438
|
+
jws: proof.jws.substring(0, 50) + '...',
|
|
439
|
+
jwsValid: proof.jws.split('.').length === 3
|
|
157
440
|
});
|
|
158
441
|
|
|
159
|
-
// Store
|
|
442
|
+
// Store in KV archive
|
|
160
443
|
if (this.proofArchive) {
|
|
161
444
|
try {
|
|
162
445
|
await this.proofArchive.store(proof, {
|
|
163
|
-
toolName: greetTool.name
|
|
164
|
-
sessionId: session.id
|
|
446
|
+
toolName: greetTool.name
|
|
165
447
|
});
|
|
166
|
-
console.log('[MCP-I] Proof stored in
|
|
448
|
+
console.log('[MCP-I] Proof stored in archive');
|
|
167
449
|
} catch (archiveError) {
|
|
168
|
-
console.error('[MCP-I]
|
|
169
|
-
|
|
450
|
+
console.error('[MCP-I] Archive error:', archiveError);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Submit to AgentShield with context
|
|
455
|
+
if (this.agentShieldConfig) {
|
|
456
|
+
try {
|
|
457
|
+
await this.submitProofToAgentShield(proof, session, greetTool.name, args, result);
|
|
458
|
+
} catch (err: any) {
|
|
459
|
+
console.error('[MCP-I] AgentShield failed:', err.message);
|
|
170
460
|
}
|
|
171
461
|
}
|
|
172
462
|
|
|
173
|
-
// Attach proof to result for MCP Inspector
|
|
174
|
-
// Following MCP-I pattern: { content: [...], _meta: { proof: {...} } }
|
|
463
|
+
// Attach proof to result for MCP Inspector
|
|
175
464
|
if (result && typeof result === 'object') {
|
|
176
465
|
(result as any)._meta = {
|
|
177
466
|
proof: {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
467
|
+
jws: proof.jws,
|
|
468
|
+
did: proof.meta.did,
|
|
469
|
+
kid: proof.meta.kid,
|
|
470
|
+
timestamp: proof.meta.ts,
|
|
471
|
+
nonce: proof.meta.nonce,
|
|
472
|
+
sessionId: proof.meta.sessionId,
|
|
473
|
+
requestHash: proof.meta.requestHash,
|
|
474
|
+
responseHash: proof.meta.responseHash
|
|
183
475
|
}
|
|
184
476
|
};
|
|
185
477
|
}
|
|
@@ -221,7 +513,7 @@ app.mount("/mcp", ${pascalClassName}MCP.serve("/mcp").fetch, { replaceRequest: f
|
|
|
221
513
|
export default app;
|
|
222
514
|
`;
|
|
223
515
|
fs.writeFileSync(path.join(srcDir, "index.ts"), indexContent);
|
|
224
|
-
// Create wrangler.toml
|
|
516
|
+
// Create wrangler.toml with optional API key
|
|
225
517
|
const wranglerContent = `#:schema node_modules/wrangler/config-schema.json
|
|
226
518
|
name = "${projectName}"
|
|
227
519
|
main = "src/index.ts"
|
|
@@ -246,8 +538,8 @@ new_sqlite_classes = ["${pascalClassName}MCP"]
|
|
|
246
538
|
#
|
|
247
539
|
# Then replace the id below with the namespace ID from the output
|
|
248
540
|
[[kv_namespaces]]
|
|
249
|
-
binding = "
|
|
250
|
-
id = "
|
|
541
|
+
binding = "${className.toUpperCase()}_NONCE_CACHE"
|
|
542
|
+
id = "your_nonce_kv_namespace_id" # Replace with actual namespace ID
|
|
251
543
|
|
|
252
544
|
# KV Namespace for proof archive (RECOMMENDED for auditability)
|
|
253
545
|
#
|
|
@@ -259,12 +551,63 @@ id = "your-nonce-kv-namespace-id" # Replace with actual namespace ID
|
|
|
259
551
|
#
|
|
260
552
|
# Note: Comment out if you don't need proof archiving
|
|
261
553
|
[[kv_namespaces]]
|
|
262
|
-
binding = "
|
|
263
|
-
id = "
|
|
554
|
+
binding = "${className.toUpperCase()}_PROOF_ARCHIVE"
|
|
555
|
+
id = "your_proof_kv_namespace_id" # Replace with actual namespace ID
|
|
556
|
+
|
|
557
|
+
# KV Namespace for identity storage (RECOMMENDED for persistent agent identity)
|
|
558
|
+
#
|
|
559
|
+
# Stores the agent's cryptographic identity (DID, keys) in KV
|
|
560
|
+
# Ensures consistent identity across Worker restarts and deployments
|
|
561
|
+
#
|
|
562
|
+
# Run: npm run kv:create-identity (creates IDENTITY_STORAGE namespace)
|
|
563
|
+
# Then replace the id below with the namespace ID from the output
|
|
564
|
+
#
|
|
565
|
+
[[kv_namespaces]]
|
|
566
|
+
binding = "${className.toUpperCase()}_IDENTITY_STORAGE"
|
|
567
|
+
id = "your_identity_kv_namespace_id" # Replace with actual namespace ID
|
|
568
|
+
|
|
569
|
+
# KV Namespace for delegation storage (REQUIRED for OAuth/delegation flows)
|
|
570
|
+
#
|
|
571
|
+
# Stores active delegations from users to agents
|
|
572
|
+
# Enables OAuth consent flows and scope-based authorization
|
|
573
|
+
#
|
|
574
|
+
# Run: npm run kv:create-delegation (creates DELEGATION_STORAGE namespace)
|
|
575
|
+
# Then replace the id below with the namespace ID from the output
|
|
576
|
+
#
|
|
577
|
+
[[kv_namespaces]]
|
|
578
|
+
binding = "${className.toUpperCase()}_DELEGATION_STORAGE"
|
|
579
|
+
id = "your_delegation_kv_namespace_id" # Replace with actual namespace ID
|
|
580
|
+
|
|
581
|
+
# KV Namespace for tool protection config (OPTIONAL for dashboard-controlled delegation)
|
|
582
|
+
#
|
|
583
|
+
# 🆕 NEW: Enables dynamic tool protection configuration from AgentShield dashboard
|
|
584
|
+
# Caches which tools require user delegation based on dashboard toggle switches
|
|
585
|
+
#
|
|
586
|
+
# Benefits:
|
|
587
|
+
# - Control tool permissions from AgentShield dashboard without code changes
|
|
588
|
+
# - Update delegation requirements in real-time
|
|
589
|
+
# - 5-minute cache reduces API calls while staying fresh
|
|
590
|
+
#
|
|
591
|
+
# Run: npm run kv:create-tool-protection (creates TOOL_PROTECTION_KV namespace)
|
|
592
|
+
# Then replace the id below with the namespace ID from the output
|
|
593
|
+
#
|
|
594
|
+
# Note: This is OPTIONAL. Comment out if you prefer hardcoded tool protections
|
|
595
|
+
# in mcpi-runtime-config.ts (see toolProtections section below)
|
|
596
|
+
#
|
|
597
|
+
[[kv_namespaces]]
|
|
598
|
+
binding = "${className.toUpperCase()}_TOOL_PROTECTION_KV"
|
|
599
|
+
id = "your_tool_protection_kv_id" # Replace with actual namespace ID
|
|
264
600
|
|
|
265
601
|
[vars]
|
|
266
602
|
XMCP_I_TS_SKEW_SEC = "120"
|
|
267
603
|
XMCP_I_SESSION_TTL = "1800"
|
|
604
|
+
|
|
605
|
+
# AgentShield Integration (https://kya.vouched.id)
|
|
606
|
+
# ${apikey ? 'Configure' : 'Uncomment and configure'} these variables to enable proof submission to AgentShield
|
|
607
|
+
AGENTSHIELD_API_URL = "https://kya.vouched.id"
|
|
608
|
+
${apikey ? `AGENTSHIELD_API_KEY = "${apikey}" # Provided via --apikey flag` : '# AGENTSHIELD_API_KEY = "sk_your_api_key_here" # Get from https://kya.vouched.id/dashboard'}
|
|
609
|
+
# AGENTSHIELD_PROJECT_ID = "your-project-id"
|
|
610
|
+
MCPI_ENV = "development"
|
|
268
611
|
`;
|
|
269
612
|
fs.writeFileSync(path.join(projectPath, "wrangler.toml"), wranglerContent);
|
|
270
613
|
// Create tsconfig.json
|
|
@@ -319,37 +662,49 @@ ${packageManager} install
|
|
|
319
662
|
|
|
320
663
|
### 2. Create KV Namespaces
|
|
321
664
|
|
|
322
|
-
#### Create
|
|
665
|
+
#### Create All KV Namespaces (Recommended)
|
|
323
666
|
|
|
324
667
|
\`\`\`bash
|
|
325
|
-
${packageManager === "npm" ? "npm run" : packageManager} kv:create
|
|
668
|
+
${packageManager === "npm" ? "npm run" : packageManager} kv:create
|
|
326
669
|
\`\`\`
|
|
327
670
|
|
|
328
|
-
|
|
671
|
+
This creates all 5 KV namespaces at once:
|
|
672
|
+
- \`NONCE_CACHE\` - Replay attack prevention (Required)
|
|
673
|
+
- \`PROOF_ARCHIVE\` - Cryptographic proof storage (Recommended)
|
|
674
|
+
- \`IDENTITY_STORAGE\` - Agent identity persistence (Recommended)
|
|
675
|
+
- \`DELEGATION_STORAGE\` - OAuth delegation storage (Required for delegation)
|
|
676
|
+
- \`TOOL_PROTECTION_KV\` - Dashboard-controlled permissions (Optional)
|
|
677
|
+
|
|
678
|
+
Copy the namespace IDs from the output and update each one in \`wrangler.toml\`:
|
|
329
679
|
|
|
330
680
|
\`\`\`toml
|
|
331
681
|
[[kv_namespaces]]
|
|
332
682
|
binding = "NONCE_CACHE"
|
|
333
|
-
id = "
|
|
334
|
-
\`\`\`
|
|
335
|
-
|
|
336
|
-
#### Create Proof Archive (Recommended)
|
|
683
|
+
id = "your_nonce_kv_id_here" # ← Update this
|
|
337
684
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
685
|
+
[[kv_namespaces]]
|
|
686
|
+
binding = "PROOF_ARCHIVE"
|
|
687
|
+
id = "your_proof_kv_id_here" # ← Update this
|
|
341
688
|
|
|
342
|
-
|
|
689
|
+
[[kv_namespaces]]
|
|
690
|
+
binding = "IDENTITY_STORAGE"
|
|
691
|
+
id = "your_identity_kv_id_here" # ← Update this
|
|
343
692
|
|
|
344
|
-
|
|
693
|
+
[[kv_namespaces]]
|
|
694
|
+
binding = "DELEGATION_STORAGE"
|
|
695
|
+
id = "your_delegation_kv_id_here" # ← Update this
|
|
345
696
|
|
|
346
|
-
\`\`\`toml
|
|
347
697
|
[[kv_namespaces]]
|
|
348
|
-
binding = "
|
|
349
|
-
id = "
|
|
698
|
+
binding = "TOOL_PROTECTION_KV"
|
|
699
|
+
id = "your_tool_protection_kv_id_here" # ← Update this
|
|
350
700
|
\`\`\`
|
|
351
701
|
|
|
352
|
-
**Note:**
|
|
702
|
+
**Note:** You can also create namespaces individually:
|
|
703
|
+
- \`${packageManager === "npm" ? "npm run" : packageManager} kv:create-nonce\` - Create nonce cache only
|
|
704
|
+
- \`${packageManager === "npm" ? "npm run" : packageManager} kv:create-proof\` - Create proof archive only
|
|
705
|
+
- \`${packageManager === "npm" ? "npm run" : packageManager} kv:create-identity\` - Create identity storage only
|
|
706
|
+
- \`${packageManager === "npm" ? "npm run" : packageManager} kv:create-delegation\` - Create delegation storage only
|
|
707
|
+
- \`${packageManager === "npm" ? "npm run" : packageManager} kv:create-tool-protection\` - Create tool protection cache only
|
|
353
708
|
|
|
354
709
|
### 3. Test Locally
|
|
355
710
|
|
|
@@ -444,10 +799,10 @@ If you configured the \`PROOF_ARCHIVE\` KV namespace, proofs are also stored for
|
|
|
444
799
|
|
|
445
800
|
\`\`\`bash
|
|
446
801
|
# List all proofs
|
|
447
|
-
wrangler kv:key list --namespace-id=
|
|
802
|
+
wrangler kv:key list --namespace-id=your_proof_kv_id
|
|
448
803
|
|
|
449
804
|
# View a specific proof
|
|
450
|
-
wrangler kv:key get "proof_1234567890_abcd" --namespace-id=
|
|
805
|
+
wrangler kv:key get "proof_1234567890_abcd" --namespace-id=your_proof_kv_id
|
|
451
806
|
\`\`\`
|
|
452
807
|
|
|
453
808
|
## Identity Management
|
|
@@ -462,6 +817,105 @@ The identity includes:
|
|
|
462
817
|
- \`publicKey\`: Ed25519 public key for signature verification
|
|
463
818
|
- \`privateKey\`: Ed25519 private key (secured in Durable Object state)
|
|
464
819
|
|
|
820
|
+
## AgentShield Integration
|
|
821
|
+
|
|
822
|
+
This project is configured to send cryptographic proofs to AgentShield for audit trails and compliance monitoring.
|
|
823
|
+
|
|
824
|
+
### Setup
|
|
825
|
+
|
|
826
|
+
1. **Get your AgentShield API key**:
|
|
827
|
+
- Sign up at https://kya.vouched.id
|
|
828
|
+
- Create a project
|
|
829
|
+
- Copy your API key from the dashboard
|
|
830
|
+
|
|
831
|
+
2. **Update \`wrangler.toml\`**:
|
|
832
|
+
\`\`\`toml
|
|
833
|
+
[vars]
|
|
834
|
+
AGENTSHIELD_API_URL = "https://kya.vouched.id"
|
|
835
|
+
AGENTSHIELD_API_KEY = "sk_your_actual_key_here" # ← Replace this
|
|
836
|
+
AGENTSHIELD_PROJECT_ID = "your-project-id" # ← Replace this
|
|
837
|
+
MCPI_ENV = "development"
|
|
838
|
+
\`\`\`
|
|
839
|
+
|
|
840
|
+
3. **Test proof submission**:
|
|
841
|
+
\`\`\`bash
|
|
842
|
+
${packageManager === "npm" ? "npm run" : packageManager} dev
|
|
843
|
+
\`\`\`
|
|
844
|
+
|
|
845
|
+
Call a tool and check the logs:
|
|
846
|
+
\`\`\`
|
|
847
|
+
[AgentShield] Submitting proof: { did: 'did:web:...', sessionId: '...', jwsFormat: 'valid (3 parts)' }
|
|
848
|
+
[AgentShield] ✅ Proofs accepted: 1
|
|
849
|
+
\`\`\`
|
|
850
|
+
|
|
851
|
+
4. **View proofs in dashboard**:
|
|
852
|
+
- Go to https://kya.vouched.id/dashboard
|
|
853
|
+
- Select your project
|
|
854
|
+
- Click "Interactions" tab
|
|
855
|
+
- See your proofs in real-time
|
|
856
|
+
|
|
857
|
+
### Configuration
|
|
858
|
+
|
|
859
|
+
The AgentShield integration is configured in \`src/mcpi-runtime-config.ts\`. You can customize:
|
|
860
|
+
- Proof batch size (\`maxBatchSize\`)
|
|
861
|
+
- Flush interval (\`flushIntervalMs\`)
|
|
862
|
+
- Retry policy (\`maxRetries\`)
|
|
863
|
+
- Tool protection rules (\`toolProtections\`)
|
|
864
|
+
|
|
865
|
+
### Dashboard-Controlled Tool Protection (Advanced)
|
|
866
|
+
|
|
867
|
+
🆕 **NEW**: Control which tools require user delegation directly from the AgentShield dashboard - no code changes needed!
|
|
868
|
+
|
|
869
|
+
Instead of hardcoding \`requiresDelegation\` in your config, enable dynamic tool protection:
|
|
870
|
+
|
|
871
|
+
1. **Create Tool Protection KV namespace**:
|
|
872
|
+
\`\`\`bash
|
|
873
|
+
${packageManager === "npm" ? "npm run" : packageManager} kv:create-tool-protection
|
|
874
|
+
\`\`\`
|
|
875
|
+
|
|
876
|
+
2. **Uncomment TOOL_PROTECTION_KV in \`wrangler.toml\`**:
|
|
877
|
+
\`\`\`toml
|
|
878
|
+
[[kv_namespaces]]
|
|
879
|
+
binding = "TOOL_PROTECTION_KV"
|
|
880
|
+
id = "your_tool_protection_kv_id" # ← Add the ID from step 1
|
|
881
|
+
\`\`\`
|
|
882
|
+
|
|
883
|
+
3. **Enable Tool Protection Service in \`src/mcpi-runtime-config.ts\`**:
|
|
884
|
+
- Uncomment the import: \`import { CloudflareRuntime } from "@kya-os/mcp-i-cloudflare";\`
|
|
885
|
+
- Uncomment the \`toolProtectionService\` configuration block
|
|
886
|
+
|
|
887
|
+
4. **Deploy and test**:
|
|
888
|
+
\`\`\`bash
|
|
889
|
+
${packageManager === "npm" ? "npm run" : packageManager} deploy
|
|
890
|
+
\`\`\`
|
|
891
|
+
|
|
892
|
+
5. **Control delegation from dashboard**:
|
|
893
|
+
- Go to https://kya.vouched.id/dashboard
|
|
894
|
+
- Select your project → "Tools" tab
|
|
895
|
+
- Toggle "Require Delegation" for any tool
|
|
896
|
+
- Changes apply in real-time (5-minute cache)
|
|
897
|
+
|
|
898
|
+
**Benefits:**
|
|
899
|
+
- Update tool permissions without redeploying
|
|
900
|
+
- Test delegation flows instantly
|
|
901
|
+
- Different requirements per environment (dev vs prod)
|
|
902
|
+
- Automatic tool discovery from proof submissions
|
|
903
|
+
|
|
904
|
+
**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.
|
|
905
|
+
|
|
906
|
+
### Disable AgentShield (Optional)
|
|
907
|
+
|
|
908
|
+
If you don't want to use AgentShield, edit \`src/mcpi-runtime-config.ts\`:
|
|
909
|
+
|
|
910
|
+
\`\`\`typescript
|
|
911
|
+
proofing: {
|
|
912
|
+
enabled: false, // Disable proof submission
|
|
913
|
+
// ...
|
|
914
|
+
}
|
|
915
|
+
\`\`\`
|
|
916
|
+
|
|
917
|
+
Or simply don't configure the \`AGENTSHIELD_API_KEY\` environment variable.
|
|
918
|
+
|
|
465
919
|
## References
|
|
466
920
|
|
|
467
921
|
- [Cloudflare Agents MCP](https://developers.cloudflare.com/agents/model-context-protocol/)
|
|
@@ -470,6 +924,28 @@ The identity includes:
|
|
|
470
924
|
`;
|
|
471
925
|
fs.writeFileSync(path.join(projectPath, "README.md"), readmeContent);
|
|
472
926
|
console.log(chalk.green("✅ Cloudflare Worker MCP server created"));
|
|
927
|
+
console.log();
|
|
928
|
+
if (apikey) {
|
|
929
|
+
console.log(chalk.green("🔑 AgentShield API key configured in wrangler.toml"));
|
|
930
|
+
console.log(chalk.dim(" Your API key has been added to the [vars] section"));
|
|
931
|
+
console.log(chalk.dim(" Remember to add your AGENTSHIELD_PROJECT_ID before deployment"));
|
|
932
|
+
console.log();
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
console.log(chalk.yellow("⚠️ No AgentShield API key provided"));
|
|
936
|
+
console.log(chalk.dim(" Add your API key to wrangler.toml [vars] section before deployment"));
|
|
937
|
+
console.log(chalk.dim(" Get your key at: https://kya.vouched.id/dashboard"));
|
|
938
|
+
console.log();
|
|
939
|
+
}
|
|
940
|
+
console.log(chalk.bold("📦 All KV Namespaces Configured"));
|
|
941
|
+
console.log(chalk.dim(" - NONCE_CACHE: Replay attack prevention"));
|
|
942
|
+
console.log(chalk.dim(" - PROOF_ARCHIVE: Cryptographic proof storage"));
|
|
943
|
+
console.log(chalk.dim(" - IDENTITY_STORAGE: Agent identity persistence"));
|
|
944
|
+
console.log(chalk.dim(" - DELEGATION_STORAGE: OAuth delegation storage"));
|
|
945
|
+
console.log(chalk.dim(" - TOOL_PROTECTION_KV: Dashboard-controlled permissions"));
|
|
946
|
+
console.log();
|
|
947
|
+
console.log(chalk.cyan(" Run 'npm run kv:create' to create all namespaces"));
|
|
948
|
+
console.log();
|
|
473
949
|
}
|
|
474
950
|
catch (error) {
|
|
475
951
|
console.error(chalk.red("Failed to set up Cloudflare Worker MCP server:"), error);
|