@kya-os/create-mcpi-app 1.8.43 → 1.8.46
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/.eslintrc.cjs +10 -0
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +4 -0
- package/.turbo/turbo-test$colon$coverage.log +605 -934
- package/.turbo/turbo-test.log +217 -222
- package/dist/helpers/create.d.ts.map +1 -1
- package/dist/helpers/create.js +0 -35
- package/dist/helpers/create.js.map +1 -1
- package/dist/helpers/fetch-cloudflare-mcpi-template.js +3 -3
- package/dist/helpers/fetch-cloudflare-mcpi-template.js.map +1 -1
- package/dist/helpers/fetch-cloudflare-template.d.ts.map +1 -1
- package/dist/helpers/fetch-cloudflare-template.js +0 -3
- package/dist/helpers/fetch-cloudflare-template.js.map +1 -1
- package/dist/helpers/generate-cloudflare-files.d.ts +87 -0
- package/dist/helpers/generate-cloudflare-files.d.ts.map +1 -0
- package/dist/helpers/generate-cloudflare-files.js +800 -0
- package/dist/helpers/generate-cloudflare-files.js.map +1 -0
- package/dist/helpers/index.d.ts +14 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +18 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/install.d.ts +1 -1
- package/dist/helpers/install.d.ts.map +1 -1
- package/dist/helpers/install.js +1 -1
- package/dist/helpers/install.js.map +1 -1
- package/package.json +22 -5
|
@@ -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 ci
|
|
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
|