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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test$colon$coverage.log +755 -0
  3. package/.turbo/turbo-test.log +200 -0
  4. package/dist/helpers/fetch-cloudflare-mcpi-template.d.ts.map +1 -1
  5. package/dist/helpers/fetch-cloudflare-mcpi-template.js +43 -912
  6. package/dist/helpers/fetch-cloudflare-mcpi-template.js.map +1 -1
  7. package/dist/utils/fetch-remote-config.d.ts.map +1 -1
  8. package/dist/utils/fetch-remote-config.js +2 -2
  9. package/dist/utils/fetch-remote-config.js.map +1 -1
  10. package/package/package.json +77 -0
  11. package/package.json +1 -1
  12. package/ARCHITECTURE_ANALYSIS.md +0 -392
  13. package/CHANGELOG.md +0 -372
  14. package/DEPRECATION_WARNINGS_ANALYSIS.md +0 -192
  15. package/IMPLEMENTATION_SUMMARY.md +0 -108
  16. package/REMEDIATION_PLAN.md +0 -99
  17. package/dist/.tsbuildinfo +0 -1
  18. package/scripts/prepare-pack.js +0 -47
  19. package/scripts/validate-no-workspace.js +0 -79
  20. package/src/__tests__/cloudflare-template.test.ts +0 -490
  21. package/src/__tests__/helpers/fetch-cloudflare-mcpi-template.test.ts +0 -337
  22. package/src/__tests__/helpers/generate-config.test.ts +0 -312
  23. package/src/__tests__/helpers/generate-identity.test.ts +0 -271
  24. package/src/__tests__/helpers/install.test.ts +0 -370
  25. package/src/__tests__/helpers/validate-project-structure.test.ts +0 -467
  26. package/src/__tests__.bak/regression.test.ts +0 -434
  27. package/src/effects/index.ts +0 -80
  28. package/src/helpers/__tests__/config-builder.spec.ts +0 -231
  29. package/src/helpers/apply-identity-preset.ts +0 -209
  30. package/src/helpers/config-builder.ts +0 -165
  31. package/src/helpers/copy-template.ts +0 -11
  32. package/src/helpers/create.ts +0 -239
  33. package/src/helpers/fetch-cloudflare-mcpi-template.ts +0 -2393
  34. package/src/helpers/fetch-cloudflare-template.ts +0 -361
  35. package/src/helpers/fetch-mcpi-template.ts +0 -236
  36. package/src/helpers/fetch-xmcp-template.ts +0 -153
  37. package/src/helpers/generate-config.ts +0 -118
  38. package/src/helpers/generate-identity.ts +0 -163
  39. package/src/helpers/identity-manager.ts +0 -186
  40. package/src/helpers/install.ts +0 -79
  41. package/src/helpers/rename.ts +0 -17
  42. package/src/helpers/validate-project-structure.ts +0 -127
  43. package/src/index.ts +0 -520
  44. package/src/utils/__tests__/fetch-remote-config.test.ts +0 -271
  45. package/src/utils/check-node.ts +0 -17
  46. package/src/utils/fetch-remote-config.ts +0 -179
  47. package/src/utils/is-folder-empty.ts +0 -60
  48. package/src/utils/validate-project-name.ts +0 -132
  49. package/test-cloudflare/README.md +0 -164
  50. package/test-cloudflare/package.json +0 -28
  51. package/test-cloudflare/src/index.ts +0 -341
  52. package/test-cloudflare/src/tools/greet.ts +0 -19
  53. package/test-cloudflare/tests/cache-invalidation.test.ts +0 -410
  54. package/test-cloudflare/tests/cors-security.test.ts +0 -349
  55. package/test-cloudflare/tests/delegation.test.ts +0 -335
  56. package/test-cloudflare/tests/do-routing.test.ts +0 -314
  57. package/test-cloudflare/tests/integration.test.ts +0 -205
  58. package/test-cloudflare/tests/session-management.test.ts +0 -359
  59. package/test-cloudflare/tsconfig.json +0 -16
  60. package/test-cloudflare/vitest.config.ts +0 -9
  61. package/test-cloudflare/wrangler.toml +0 -37
  62. package/test-node/README.md +0 -44
  63. package/test-node/package.json +0 -23
  64. package/test-node/src/tools/greet.ts +0 -25
  65. package/test-node/xmcp.config.ts +0 -20
  66. package/tsconfig.json +0 -26
  67. package/vitest.config.ts +0 -14
@@ -1,164 +0,0 @@
1
- # test-cloudflare
2
-
3
- MCP-I server running on Cloudflare Workers with identity verification.
4
-
5
- ## Quick Start
6
-
7
- ### 1. Install Dependencies
8
-
9
- ```bash
10
- npm install
11
- ```
12
-
13
- ### 2. Create KV Namespace
14
-
15
- ```bash
16
- npm run kv:create
17
- ```
18
-
19
- This runs: `wrangler kv namespace create NONCE_CACHE`
20
-
21
- Copy the `id` from the output and update `wrangler.toml`:
22
-
23
- ```toml
24
- [[kv_namespaces]]
25
- binding = "NONCE_CACHE"
26
- id = "your-actual-kv-id-here" # ← Update this
27
- ```
28
-
29
- ### 3. Test Locally
30
-
31
- ```bash
32
- npm run dev
33
- ```
34
-
35
- **Endpoints:**
36
- - MCP: http://localhost:8787/mcp
37
- - Verify: http://localhost:8787/verify
38
- - Health: http://localhost:8787/health
39
-
40
- ### 4. Deploy to Cloudflare
41
-
42
- ```bash
43
- # Login to Cloudflare (first time only)
44
- npx wrangler login
45
-
46
- # Deploy to development
47
- npm run deploy
48
-
49
- # Deploy to production
50
- npm run deploy --env production
51
- ```
52
-
53
- ## Connect with Claude Desktop
54
-
55
- Add to your `claude_desktop_config.json`:
56
-
57
- ```json
58
- {
59
- "mcpServers": {
60
- "test-cloudflare": {
61
- "command": "npx",
62
- "args": [
63
- "mcp-remote",
64
- "https://your-worker.workers.dev/mcp"
65
- ]
66
- }
67
- }
68
- }
69
- ```
70
-
71
- ## Adding Tools
72
-
73
- Create new tools in `src/tools/`:
74
-
75
- ```typescript
76
- // src/tools/example.ts
77
- import { z } from "zod";
78
- import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/types.js";
79
-
80
- export const exampleTool = {
81
- name: "example",
82
- description: "Example tool description",
83
- inputSchema: z.object({
84
- input: z.string().describe("Input parameter"),
85
- }),
86
- handler: async (args: { [key: string]: any }, extra: RequestHandlerExtra<any, any>) => {
87
- const { input } = args as { input: string };
88
- return {
89
- content: [
90
- {
91
- type: "text" as const,
92
- text: `Result: ${input}`
93
- }
94
- ],
95
- };
96
- },
97
- };
98
- ```
99
-
100
- Register in `src/index.ts`:
101
-
102
- ```typescript
103
- import { exampleTool } from "./tools/example";
104
-
105
- async init() {
106
- this.server.tool(
107
- exampleTool.name,
108
- exampleTool.description,
109
- exampleTool.inputSchema.shape,
110
- exampleTool.handler
111
- );
112
- }
113
- ```
114
-
115
- ## API Endpoints
116
-
117
- ### `GET /health`
118
- Health check endpoint.
119
-
120
- **Response:**
121
- ```json
122
- {
123
- "status": "healthy",
124
- "timestamp": "2024-10-10T12:00:00.000Z"
125
- }
126
- ```
127
-
128
- ### `POST /mcp`
129
- MCP protocol endpoint for tool calls.
130
-
131
- ### `POST /verify`
132
- Verify MCP-I cryptographic proofs.
133
-
134
- **Request:**
135
- ```json
136
- {
137
- "proof": {
138
- "agentDid": "did:web:example.com",
139
- "timestamp": 1234567890,
140
- "nonce": "abc123",
141
- "signature": "...",
142
- "publicKey": "..."
143
- }
144
- }
145
- ```
146
-
147
- **Response:**
148
- ```json
149
- {
150
- "verified": true,
151
- "agent": {
152
- "did": "did:web:example.com",
153
- "keyId": "key-123",
154
- "scopes": ["read", "write"],
155
- "session": "session-456"
156
- }
157
- }
158
- ```
159
-
160
- ## Learn More
161
-
162
- - [MCP-I Documentation](https://github.com/kya-os/xmcp-i)
163
- - [Cloudflare Workers](https://developers.cloudflare.com/workers/)
164
- - [agents/mcp](https://www.npmjs.com/package/agents)
@@ -1,28 +0,0 @@
1
- {
2
- "name": "test-cloudflare",
3
- "version": "0.1.0",
4
- "private": true,
5
- "scripts": {
6
- "deploy": "wrangler deploy",
7
- "dev": "wrangler dev",
8
- "start": "wrangler dev",
9
- "test": "vitest run",
10
- "test:watch": "vitest",
11
- "kv:create": "wrangler kv namespace create NONCE_CACHE",
12
- "kv:list": "wrangler kv namespace list",
13
- "type-check": "tsc --noEmit"
14
- },
15
- "dependencies": {
16
- "@kya-os/mcp-i-cloudflare": "^1.0.0",
17
- "@modelcontextprotocol/sdk": "^1.19.1",
18
- "agents": "^0.2.8",
19
- "hono": "^4.9.10",
20
- "zod": "^3.25.76"
21
- },
22
- "devDependencies": {
23
- "@cloudflare/workers-types": "^4.20240925.0",
24
- "typescript": "^5.6.2",
25
- "vitest": "^2.1.8",
26
- "wrangler": "^4.42.2"
27
- }
28
- }
@@ -1,341 +0,0 @@
1
- /// <reference types="@cloudflare/workers-types" />
2
- import { McpAgent } from "agents/mcp";
3
- import { Hono } from "hono";
4
- import { cors } from "hono/cors";
5
- import { createCloudflareRuntime, KVProofArchive } from "@kya-os/mcp-i-cloudflare";
6
- import type { MCPIRuntimeBase } from "@kya-os/mcp-i-core";
7
- import { WELL_KNOWN_CORS_HEADERS } from "@kya-os/mcp-i-core";
8
- import { greetTool } from "./tools/greet";
9
-
10
- interface Env {
11
- MCP_OBJECT: DurableObjectNamespace;
12
- NONCE_CACHE: KVNamespace;
13
- PROOF_ARCHIVE?: KVNamespace;
14
- XMCP_I_TS_SKEW_SEC?: string;
15
- XMCP_I_SESSION_TTL?: string;
16
- ADMIN_PROOFS_ENABLED?: string;
17
- MCP_IDENTITY_PRIVATE_KEY?: string;
18
- MCP_IDENTITY_PUBLIC_KEY?: string;
19
- MCP_IDENTITY_AGENT_DID?: string;
20
- }
21
-
22
- export class CloudflareMCP extends McpAgent {
23
- private mcpiRuntime?: MCPIRuntimeBase;
24
- private proofArchive?: KVProofArchive;
25
- private sessionId?: string;
26
-
27
- constructor(state: DurableObjectState, env: Env) {
28
- super(state, env);
29
-
30
- this.mcpiRuntime = createCloudflareRuntime({
31
- env: env as any,
32
- environment: 'development',
33
- timestampSkewSeconds: parseInt(env.XMCP_I_TS_SKEW_SEC || '120'),
34
- sessionTtlMinutes: parseInt(env.XMCP_I_SESSION_TTL || '1800') / 60,
35
- audit: {
36
- enabled: true
37
- }
38
- });
39
-
40
- if (env.PROOF_ARCHIVE) {
41
- this.proofArchive = new KVProofArchive(env.PROOF_ARCHIVE, {
42
- ttl: 86400 * 30
43
- });
44
- }
45
- }
46
-
47
- async init() {
48
- await this.mcpiRuntime?.initialize();
49
-
50
- this.server.tool(
51
- greetTool.name,
52
- greetTool.description,
53
- greetTool.inputSchema.shape,
54
- async (args: any) => {
55
- const identity = await this.mcpiRuntime!.getIdentity();
56
- const sessionId = await this.ensureSessionId();
57
- const establishedAt = await this.state.storage.get<number>("establishedAt") ?? Date.now();
58
- const nonce = await this.mcpiRuntime!.issueNonce(sessionId);
59
-
60
- const sessionContext = {
61
- sessionId,
62
- agentDid: identity.did,
63
- kid: identity.kid,
64
- establishedAt,
65
- lastActivityAt: Date.now(),
66
- nonce,
67
- };
68
-
69
- const result = await this.mcpiRuntime!.processToolCall(
70
- greetTool.name,
71
- args,
72
- greetTool.handler,
73
- sessionContext
74
- );
75
-
76
- if (this.proofArchive) {
77
- const proof = this.mcpiRuntime!.getLastProof();
78
- if (proof) {
79
- await this.proofArchive.store(proof, { toolName: greetTool.name, sessionId });
80
- }
81
- }
82
-
83
- return result;
84
- }
85
- );
86
- }
87
-
88
- private async ensureSessionId(): Promise<string> {
89
- if (!this.sessionId) {
90
- this.sessionId = await this.state.storage.get<string>("sessionId");
91
- if (!this.sessionId) {
92
- this.sessionId = crypto.randomUUID();
93
- await this.state.storage.put("sessionId", this.sessionId);
94
- await this.state.storage.put("establishedAt", Date.now());
95
- }
96
- }
97
- return this.sessionId;
98
- }
99
-
100
- async fetch(request: Request): Promise<Response> {
101
- const url = new URL(request.url);
102
-
103
- if (url.pathname === '/_internal/identity') {
104
- const identity = await this.mcpiRuntime!.getIdentity();
105
- return new Response(JSON.stringify({
106
- did: identity.did,
107
- publicKey: identity.publicKey,
108
- kid: identity.kid
109
- }), {
110
- headers: { 'Content-Type': 'application/json' }
111
- });
112
- }
113
-
114
- if (url.pathname.startsWith('/_internal/well-known/')) {
115
- const handler = this.mcpiRuntime!.createWellKnownHandler({
116
- serviceName: 'test-cloudflare',
117
- serviceEndpoint: request.headers.get('X-Service-Origin') || url.origin
118
- });
119
- const path = url.pathname.replace('/_internal/well-known/', '/.well-known/');
120
- const result = await handler(path);
121
-
122
- if (result?.status && result?.headers && result?.body) {
123
- return new Response(result.body, {
124
- status: result.status,
125
- headers: result.headers
126
- });
127
- }
128
-
129
- return new Response(JSON.stringify(result), {
130
- headers: { 'Content-Type': 'application/json' }
131
- });
132
- }
133
-
134
- if (url.pathname === '/_internal/verify') {
135
- const body = await request.json() as { data: any; proof: any };
136
- const isValid = await this.mcpiRuntime!.verifyProof(body.data, body.proof);
137
- return new Response(JSON.stringify({ valid: isValid }), {
138
- headers: { 'Content-Type': 'application/json' }
139
- });
140
- }
141
-
142
- if (url.pathname === '/_internal/proofs') {
143
- if (!this.proofArchive) {
144
- return new Response(JSON.stringify({ error: 'Proof archive not initialized' }), {
145
- status: 500,
146
- headers: { 'Content-Type': 'application/json' }
147
- });
148
- }
149
-
150
- const limit = parseInt(url.searchParams.get('limit') || '10');
151
- const sessionId = url.searchParams.get('sessionId');
152
-
153
- const proofs = sessionId
154
- ? await this.proofArchive.getBySession(sessionId, limit)
155
- : await this.proofArchive.getRecent(limit);
156
-
157
- return new Response(JSON.stringify({ proofs }), {
158
- headers: { 'Content-Type': 'application/json' }
159
- });
160
- }
161
-
162
- if (url.pathname === '/_internal/proofs/tail') {
163
- if (!this.proofArchive) {
164
- return new Response(JSON.stringify({ error: 'Proof archive not initialized' }), {
165
- status: 500,
166
- headers: { 'Content-Type': 'application/json' }
167
- });
168
- }
169
-
170
- const sessionId = url.searchParams.get('session') ?? '';
171
- const limit = parseInt(url.searchParams.get('limit') || '10');
172
- let iv: ReturnType<typeof setInterval> | undefined;
173
-
174
- const stream = new ReadableStream({
175
- start: async (controller) => {
176
- const enc = new TextEncoder();
177
- let lastSeenId: string | null = null;
178
-
179
- const send = (p: any) => {
180
- const data = `event: mcpi-proof\ndata: ${JSON.stringify(p)}\n\n`;
181
- controller.enqueue(enc.encode(data));
182
- };
183
-
184
- const poll = async () => {
185
- try {
186
- const list = sessionId
187
- ? await this.proofArchive!.getBySession(sessionId, limit)
188
- : await this.proofArchive!.getRecent(limit);
189
-
190
- for (const p of list) {
191
- if (!lastSeenId || p.id > lastSeenId) {
192
- send(p);
193
- lastSeenId = p.id;
194
- }
195
- }
196
- } catch (error) {
197
- console.error('Error polling proofs:', error);
198
- }
199
- };
200
-
201
- await poll();
202
- iv = setInterval(poll, 500);
203
- },
204
- cancel() {
205
- if (iv) clearInterval(iv);
206
- }
207
- });
208
-
209
- return new Response(stream, {
210
- headers: {
211
- 'Content-Type': 'text/event-stream',
212
- 'Cache-Control': 'no-cache',
213
- 'Connection': 'keep-alive'
214
- }
215
- });
216
- }
217
-
218
- return super.fetch(request);
219
- }
220
- }
221
-
222
- const app = new Hono<{ Bindings: Env }>();
223
-
224
- app.use("/*", cors({
225
- origin: "*",
226
- allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
227
- allowHeaders: ["Content-Type", "Authorization", "mcp-session-id", "mcp-protocol-version"],
228
- exposeHeaders: ["mcp-session-id"],
229
- }));
230
-
231
- app.get("/health", (c) => c.json({
232
- status: 'healthy',
233
- timestamp: new Date().toISOString(),
234
- transport: { sse: '/sse', streamableHttp: '/mcp' }
235
- }));
236
-
237
- app.get("/.well-known/did.json", async (c) => {
238
- const stub = c.env.MCP_OBJECT.get(c.env.MCP_OBJECT.idFromName("singleton"));
239
- const origin = new URL(c.req.url).origin;
240
- const response = await stub.fetch("https://do/_internal/well-known/did.json", {
241
- headers: { 'X-Service-Origin': origin }
242
- });
243
-
244
- return new Response(response.body, {
245
- status: response.status,
246
- headers: {
247
- ...Object.fromEntries(response.headers),
248
- ...WELL_KNOWN_CORS_HEADERS
249
- }
250
- });
251
- });
252
-
253
- app.get("/.well-known/agent.json", async (c) => {
254
- const stub = c.env.MCP_OBJECT.get(c.env.MCP_OBJECT.idFromName("singleton"));
255
- const origin = new URL(c.req.url).origin;
256
- const response = await stub.fetch("https://do/_internal/well-known/agent.json", {
257
- headers: { 'X-Service-Origin': origin }
258
- });
259
-
260
- return new Response(response.body, {
261
- status: response.status,
262
- headers: {
263
- ...Object.fromEntries(response.headers),
264
- ...WELL_KNOWN_CORS_HEADERS
265
- }
266
- });
267
- });
268
-
269
- app.get("/.well-known/mcp-identity", async (c) => {
270
- const stub = c.env.MCP_OBJECT.get(c.env.MCP_OBJECT.idFromName("singleton"));
271
- const origin = new URL(c.req.url).origin;
272
- const response = await stub.fetch("https://do/_internal/well-known/mcp-identity", {
273
- headers: { 'X-Service-Origin': origin }
274
- });
275
-
276
- return new Response(response.body, {
277
- status: response.status,
278
- headers: {
279
- ...Object.fromEntries(response.headers),
280
- ...WELL_KNOWN_CORS_HEADERS
281
- }
282
- });
283
- });
284
-
285
- app.post("/verify", async (c) => {
286
- const stub = c.env.MCP_OBJECT.get(c.env.MCP_OBJECT.idFromName("singleton"));
287
- const body = await c.req.json();
288
-
289
- const response = await stub.fetch("https://do/_internal/verify", {
290
- method: 'POST',
291
- headers: { 'Content-Type': 'application/json' },
292
- body: JSON.stringify(body)
293
- });
294
-
295
- return c.json(await response.json());
296
- });
297
-
298
- app.get("/admin/proofs", async (c) => {
299
- if (c.env.ADMIN_PROOFS_ENABLED !== 'true') {
300
- return c.json({ error: 'Admin endpoint disabled' }, 403);
301
- }
302
-
303
- const stub = c.env.MCP_OBJECT.get(c.env.MCP_OBJECT.idFromName("singleton"));
304
- const limit = c.req.query('limit') || '10';
305
- const sessionId = c.req.query('sessionId');
306
-
307
- const url = new URL("https://do/_internal/proofs");
308
- url.searchParams.set('limit', limit);
309
- if (sessionId) {
310
- url.searchParams.set('sessionId', sessionId);
311
- }
312
-
313
- const response = await stub.fetch(url.toString());
314
- return c.json(await response.json());
315
- });
316
-
317
- app.get("/proofs", async (c) => {
318
- const stub = c.env.MCP_OBJECT.get(c.env.MCP_OBJECT.idFromName("singleton"));
319
- const sessionId = c.req.query('session') ?? '';
320
- const limit = c.req.query('limit') || '10';
321
-
322
- const url = new URL("https://do/_internal/proofs/tail");
323
- url.searchParams.set('session', sessionId);
324
- url.searchParams.set('limit', limit);
325
-
326
- const response = await stub.fetch(url.toString());
327
-
328
- return new Response(response.body, {
329
- headers: {
330
- 'Content-Type': 'text/event-stream',
331
- 'Cache-Control': 'no-cache',
332
- 'Connection': 'keep-alive',
333
- 'Access-Control-Allow-Origin': '*'
334
- }
335
- });
336
- });
337
-
338
- app.mount("/sse", CloudflareMCP.serveSSE("/sse").fetch, { replaceRequest: false });
339
- app.mount("/mcp", CloudflareMCP.serve("/mcp").fetch, { replaceRequest: false });
340
-
341
- export default app;
@@ -1,19 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const greetTool = {
4
- name: "greet",
5
- description: "Greet a user by name",
6
- inputSchema: z.object({
7
- name: z.string().describe("The name of the user to greet")
8
- }),
9
- handler: async ({ name }: { name: string }) => {
10
- return {
11
- content: [
12
- {
13
- type: "text" as const,
14
- text: `Hello, ${name}! Welcome to your Cloudflare MCP server.`
15
- }
16
- ]
17
- };
18
- }
19
- };