@kya-os/create-mcpi-app 1.7.38-canary.2 → 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.
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test$colon$coverage.log +755 -0
- package/.turbo/turbo-test.log +200 -0
- package/dist/helpers/fetch-cloudflare-mcpi-template.d.ts.map +1 -1
- package/dist/helpers/fetch-cloudflare-mcpi-template.js +35 -914
- package/dist/helpers/fetch-cloudflare-mcpi-template.js.map +1 -1
- package/dist/utils/fetch-remote-config.d.ts.map +1 -1
- package/dist/utils/fetch-remote-config.js +2 -2
- package/dist/utils/fetch-remote-config.js.map +1 -1
- package/package/package.json +77 -0
- package/package.json +1 -1
- package/ARCHITECTURE_ANALYSIS.md +0 -392
- package/CHANGELOG.md +0 -372
- package/DEPRECATION_WARNINGS_ANALYSIS.md +0 -192
- package/IMPLEMENTATION_SUMMARY.md +0 -108
- package/REMEDIATION_PLAN.md +0 -99
- package/dist/.tsbuildinfo +0 -1
- package/scripts/prepare-pack.js +0 -47
- package/scripts/validate-no-workspace.js +0 -79
- package/src/__tests__/cloudflare-template.test.ts +0 -490
- package/src/__tests__/helpers/fetch-cloudflare-mcpi-template.test.ts +0 -337
- package/src/__tests__/helpers/generate-config.test.ts +0 -312
- package/src/__tests__/helpers/generate-identity.test.ts +0 -271
- package/src/__tests__/helpers/install.test.ts +0 -370
- package/src/__tests__/helpers/validate-project-structure.test.ts +0 -467
- package/src/__tests__.bak/regression.test.ts +0 -434
- package/src/effects/index.ts +0 -80
- package/src/helpers/__tests__/config-builder.spec.ts +0 -231
- package/src/helpers/apply-identity-preset.ts +0 -209
- package/src/helpers/config-builder.ts +0 -165
- package/src/helpers/copy-template.ts +0 -11
- package/src/helpers/create.ts +0 -239
- package/src/helpers/fetch-cloudflare-mcpi-template.ts +0 -2404
- package/src/helpers/fetch-cloudflare-template.ts +0 -361
- package/src/helpers/fetch-mcpi-template.ts +0 -236
- package/src/helpers/fetch-xmcp-template.ts +0 -153
- package/src/helpers/generate-config.ts +0 -118
- package/src/helpers/generate-identity.ts +0 -163
- package/src/helpers/identity-manager.ts +0 -186
- package/src/helpers/install.ts +0 -79
- package/src/helpers/rename.ts +0 -17
- package/src/helpers/validate-project-structure.ts +0 -127
- package/src/index.ts +0 -520
- package/src/utils/__tests__/fetch-remote-config.test.ts +0 -271
- package/src/utils/check-node.ts +0 -17
- package/src/utils/fetch-remote-config.ts +0 -179
- package/src/utils/is-folder-empty.ts +0 -60
- package/src/utils/validate-project-name.ts +0 -132
- package/test-cloudflare/README.md +0 -164
- package/test-cloudflare/package.json +0 -28
- package/test-cloudflare/src/index.ts +0 -341
- package/test-cloudflare/src/tools/greet.ts +0 -19
- package/test-cloudflare/tests/cache-invalidation.test.ts +0 -410
- package/test-cloudflare/tests/cors-security.test.ts +0 -349
- package/test-cloudflare/tests/delegation.test.ts +0 -335
- package/test-cloudflare/tests/do-routing.test.ts +0 -314
- package/test-cloudflare/tests/integration.test.ts +0 -205
- package/test-cloudflare/tests/session-management.test.ts +0 -359
- package/test-cloudflare/tsconfig.json +0 -16
- package/test-cloudflare/vitest.config.ts +0 -9
- package/test-cloudflare/wrangler.toml +0 -37
- package/test-node/README.md +0 -44
- package/test-node/package.json +0 -23
- package/test-node/src/tools/greet.ts +0 -25
- package/test-node/xmcp.config.ts +0 -20
- package/tsconfig.json +0 -26
- package/vitest.config.ts +0 -14
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, beforeEach } from 'vitest';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Durable Object Routing Tests
|
|
5
|
-
* Tests the multi-instance DO routing for horizontal scaling
|
|
6
|
-
*/
|
|
7
|
-
describe('Durable Object Multi-Instance Routing', () => {
|
|
8
|
-
|
|
9
|
-
// Mock the getDoInstanceId function
|
|
10
|
-
function getDoInstanceId(request: Request, env: any): string {
|
|
11
|
-
const strategy = env.DO_ROUTING_STRATEGY || 'session';
|
|
12
|
-
const headers = request.headers;
|
|
13
|
-
|
|
14
|
-
switch (strategy) {
|
|
15
|
-
case 'session': {
|
|
16
|
-
const sessionId = headers.get('mcp-session-id') ||
|
|
17
|
-
headers.get('Mcp-Session-Id') ||
|
|
18
|
-
crypto.randomUUID();
|
|
19
|
-
return `session:${sessionId}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
case 'shard': {
|
|
23
|
-
const identifier = headers.get('mcp-session-id') || Math.random().toString();
|
|
24
|
-
let hash = 0;
|
|
25
|
-
for (let i = 0; i < identifier.length; i++) {
|
|
26
|
-
hash = ((hash << 5) - hash) + identifier.charCodeAt(i);
|
|
27
|
-
hash = hash & hash;
|
|
28
|
-
}
|
|
29
|
-
const shardCount = parseInt(env.DO_SHARD_COUNT || '10');
|
|
30
|
-
// Validate shard count - must be a valid positive number
|
|
31
|
-
const validShardCount = (!isNaN(shardCount) && shardCount > 0) ? shardCount : 10;
|
|
32
|
-
const shard = Math.abs(hash) % validShardCount;
|
|
33
|
-
return `shard:${shard}`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
default:
|
|
37
|
-
return 'default';
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
describe('Session-Based Routing', () => {
|
|
42
|
-
const env = {
|
|
43
|
-
DO_ROUTING_STRATEGY: 'session',
|
|
44
|
-
DO_SHARD_COUNT: '10'
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
test('should route to different instances for different sessions', () => {
|
|
48
|
-
const req1 = new Request('http://test/mcp', {
|
|
49
|
-
headers: { 'mcp-session-id': 'session-123' }
|
|
50
|
-
});
|
|
51
|
-
const req2 = new Request('http://test/mcp', {
|
|
52
|
-
headers: { 'mcp-session-id': 'session-456' }
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const id1 = getDoInstanceId(req1, env);
|
|
56
|
-
const id2 = getDoInstanceId(req2, env);
|
|
57
|
-
|
|
58
|
-
expect(id1).toBe('session:session-123');
|
|
59
|
-
expect(id2).toBe('session:session-456');
|
|
60
|
-
expect(id1).not.toBe(id2);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('should route to same instance for same session', () => {
|
|
64
|
-
const sessionId = 'consistent-session';
|
|
65
|
-
const req1 = new Request('http://test/mcp', {
|
|
66
|
-
headers: { 'mcp-session-id': sessionId }
|
|
67
|
-
});
|
|
68
|
-
const req2 = new Request('http://test/mcp', {
|
|
69
|
-
headers: { 'mcp-session-id': sessionId }
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const id1 = getDoInstanceId(req1, env);
|
|
73
|
-
const id2 = getDoInstanceId(req2, env);
|
|
74
|
-
|
|
75
|
-
expect(id1).toBe(id2);
|
|
76
|
-
expect(id1).toBe(`session:${sessionId}`);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('should handle both mcp-session-id header casings', () => {
|
|
80
|
-
const sessionId = 'case-test-session';
|
|
81
|
-
const req1 = new Request('http://test/mcp', {
|
|
82
|
-
headers: { 'mcp-session-id': sessionId }
|
|
83
|
-
});
|
|
84
|
-
const req2 = new Request('http://test/mcp', {
|
|
85
|
-
headers: { 'Mcp-Session-Id': sessionId }
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const id1 = getDoInstanceId(req1, env);
|
|
89
|
-
const id2 = getDoInstanceId(req2, env);
|
|
90
|
-
|
|
91
|
-
expect(id1).toBe(`session:${sessionId}`);
|
|
92
|
-
expect(id2).toBe(`session:${sessionId}`);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('should generate random session if header missing', () => {
|
|
96
|
-
const req = new Request('http://test/mcp', {
|
|
97
|
-
headers: {}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const id = getDoInstanceId(req, env);
|
|
101
|
-
|
|
102
|
-
expect(id).toMatch(/^session:[0-9a-f-]+$/);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
describe('Shard-Based Routing', () => {
|
|
107
|
-
const env = {
|
|
108
|
-
DO_ROUTING_STRATEGY: 'shard',
|
|
109
|
-
DO_SHARD_COUNT: '10'
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
test('should distribute requests across shards', () => {
|
|
113
|
-
const distribution = new Map<string, number>();
|
|
114
|
-
|
|
115
|
-
// Generate 1000 requests with different sessions
|
|
116
|
-
for (let i = 0; i < 1000; i++) {
|
|
117
|
-
const req = new Request('http://test/mcp', {
|
|
118
|
-
headers: { 'mcp-session-id': `session-${i}` }
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const instanceId = getDoInstanceId(req, env);
|
|
122
|
-
const shard = instanceId.split(':')[1];
|
|
123
|
-
|
|
124
|
-
distribution.set(shard, (distribution.get(shard) || 0) + 1);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Verify reasonable distribution across 10 shards
|
|
128
|
-
expect(distribution.size).toBeGreaterThanOrEqual(8); // At least 8 shards used
|
|
129
|
-
|
|
130
|
-
// Each shard should get 50-150 requests (allowing for hash variance)
|
|
131
|
-
for (const count of distribution.values()) {
|
|
132
|
-
expect(count).toBeGreaterThan(50);
|
|
133
|
-
expect(count).toBeLessThan(150);
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test('should consistently route same session to same shard', () => {
|
|
138
|
-
const sessionId = 'shard-consistent-session';
|
|
139
|
-
const results = new Set<string>();
|
|
140
|
-
|
|
141
|
-
// Make 10 requests with same session
|
|
142
|
-
for (let i = 0; i < 10; i++) {
|
|
143
|
-
const req = new Request('http://test/mcp', {
|
|
144
|
-
headers: { 'mcp-session-id': sessionId }
|
|
145
|
-
});
|
|
146
|
-
const instanceId = getDoInstanceId(req, env);
|
|
147
|
-
results.add(instanceId);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Should all go to same shard
|
|
151
|
-
expect(results.size).toBe(1);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test('should respect custom shard count', () => {
|
|
155
|
-
const customEnv = {
|
|
156
|
-
DO_ROUTING_STRATEGY: 'shard',
|
|
157
|
-
DO_SHARD_COUNT: '5'
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
const shards = new Set<number>();
|
|
161
|
-
|
|
162
|
-
for (let i = 0; i < 100; i++) {
|
|
163
|
-
const req = new Request('http://test/mcp', {
|
|
164
|
-
headers: { 'mcp-session-id': `test-${i}` }
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
const instanceId = getDoInstanceId(req, customEnv);
|
|
168
|
-
const shardNum = parseInt(instanceId.split(':')[1]);
|
|
169
|
-
shards.add(shardNum);
|
|
170
|
-
|
|
171
|
-
// Shard should be 0-4 for 5 shards
|
|
172
|
-
expect(shardNum).toBeGreaterThanOrEqual(0);
|
|
173
|
-
expect(shardNum).toBeLessThan(5);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Should use most shards
|
|
177
|
-
expect(shards.size).toBeGreaterThanOrEqual(4);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
describe('Default Fallback', () => {
|
|
182
|
-
test('should fall back to default when strategy unknown', () => {
|
|
183
|
-
const env = {
|
|
184
|
-
DO_ROUTING_STRATEGY: 'unknown-strategy',
|
|
185
|
-
DO_SHARD_COUNT: '10'
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const req = new Request('http://test/mcp', {
|
|
189
|
-
headers: { 'mcp-session-id': 'test-session' }
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
const instanceId = getDoInstanceId(req, env);
|
|
193
|
-
|
|
194
|
-
expect(instanceId).toBe('default');
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test('should use session strategy when not configured', () => {
|
|
198
|
-
const env = {}; // No configuration
|
|
199
|
-
|
|
200
|
-
const req = new Request('http://test/mcp', {
|
|
201
|
-
headers: { 'mcp-session-id': 'test-session' }
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
const instanceId = getDoInstanceId(req, env);
|
|
205
|
-
|
|
206
|
-
expect(instanceId).toBe('session:test-session');
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
describe('Performance Characteristics', () => {
|
|
211
|
-
test('session routing should have O(1) complexity', () => {
|
|
212
|
-
const env = { DO_ROUTING_STRATEGY: 'session' };
|
|
213
|
-
const sessionId = 'perf-test-session';
|
|
214
|
-
|
|
215
|
-
const start = performance.now();
|
|
216
|
-
|
|
217
|
-
for (let i = 0; i < 10000; i++) {
|
|
218
|
-
const req = new Request('http://test/mcp', {
|
|
219
|
-
headers: { 'mcp-session-id': sessionId }
|
|
220
|
-
});
|
|
221
|
-
getDoInstanceId(req, env);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const duration = performance.now() - start;
|
|
225
|
-
|
|
226
|
-
// Should complete 10k operations in < 300ms (relaxed for CI environments)
|
|
227
|
-
expect(duration).toBeLessThan(300);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
test('shard routing should have O(n) complexity for hash', () => {
|
|
231
|
-
const env = {
|
|
232
|
-
DO_ROUTING_STRATEGY: 'shard',
|
|
233
|
-
DO_SHARD_COUNT: '100'
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
const shortId = 'abc';
|
|
237
|
-
const longId = 'a'.repeat(1000);
|
|
238
|
-
|
|
239
|
-
const start1 = performance.now();
|
|
240
|
-
for (let i = 0; i < 1000; i++) {
|
|
241
|
-
const req = new Request('http://test/mcp', {
|
|
242
|
-
headers: { 'mcp-session-id': shortId }
|
|
243
|
-
});
|
|
244
|
-
getDoInstanceId(req, env);
|
|
245
|
-
}
|
|
246
|
-
const shortDuration = performance.now() - start1;
|
|
247
|
-
|
|
248
|
-
const start2 = performance.now();
|
|
249
|
-
for (let i = 0; i < 1000; i++) {
|
|
250
|
-
const req = new Request('http://test/mcp', {
|
|
251
|
-
headers: { 'mcp-session-id': longId }
|
|
252
|
-
});
|
|
253
|
-
getDoInstanceId(req, env);
|
|
254
|
-
}
|
|
255
|
-
const longDuration = performance.now() - start2;
|
|
256
|
-
|
|
257
|
-
// Longer IDs should take proportionally more time (but allow for timing variance)
|
|
258
|
-
// Note: O(n) complexity is hard to measure reliably in JavaScript due to V8 optimizations
|
|
259
|
-
// Instead, we just verify that both operations complete successfully
|
|
260
|
-
expect(longDuration).toBeGreaterThan(0);
|
|
261
|
-
expect(shortDuration).toBeGreaterThan(0);
|
|
262
|
-
// Both operations should complete in reasonable time (< 100ms total for 1000 iterations)
|
|
263
|
-
expect(longDuration + shortDuration).toBeLessThan(100);
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
describe('Edge Cases', () => {
|
|
268
|
-
test('should handle empty session ID', () => {
|
|
269
|
-
const env = { DO_ROUTING_STRATEGY: 'session' };
|
|
270
|
-
|
|
271
|
-
const req = new Request('http://test/mcp', {
|
|
272
|
-
headers: { 'mcp-session-id': '' }
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
const instanceId = getDoInstanceId(req, env);
|
|
276
|
-
|
|
277
|
-
// Should generate new session
|
|
278
|
-
expect(instanceId).toMatch(/^session:[0-9a-f-]+$/);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
test('should handle non-numeric shard count', () => {
|
|
282
|
-
const env = {
|
|
283
|
-
DO_ROUTING_STRATEGY: 'shard',
|
|
284
|
-
DO_SHARD_COUNT: 'invalid'
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
const req = new Request('http://test/mcp', {
|
|
288
|
-
headers: { 'mcp-session-id': 'test' }
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// Should default to 10 shards
|
|
292
|
-
const instanceId = getDoInstanceId(req, env);
|
|
293
|
-
const shard = parseInt(instanceId.split(':')[1]);
|
|
294
|
-
|
|
295
|
-
expect(shard).toBeGreaterThanOrEqual(0);
|
|
296
|
-
expect(shard).toBeLessThan(10);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
test('should handle special characters in session ID', () => {
|
|
300
|
-
const env = { DO_ROUTING_STRATEGY: 'shard' };
|
|
301
|
-
const specialId = '!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
302
|
-
|
|
303
|
-
const req = new Request('http://test/mcp', {
|
|
304
|
-
headers: { 'mcp-session-id': specialId }
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
// Should not throw and produce valid shard
|
|
308
|
-
expect(() => getDoInstanceId(req, env)).not.toThrow();
|
|
309
|
-
|
|
310
|
-
const instanceId = getDoInstanceId(req, env);
|
|
311
|
-
expect(instanceId).toMatch(/^shard:\d+$/);
|
|
312
|
-
});
|
|
313
|
-
});
|
|
314
|
-
});
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
-
import { unstable_dev } from 'wrangler';
|
|
3
|
-
import type { UnstableDevWorker } from 'wrangler';
|
|
4
|
-
|
|
5
|
-
describe('MCP-I Cloudflare Integration', () => {
|
|
6
|
-
let worker: UnstableDevWorker;
|
|
7
|
-
let baseUrl: string;
|
|
8
|
-
|
|
9
|
-
beforeAll(async () => {
|
|
10
|
-
worker = await unstable_dev('src/index.ts', {
|
|
11
|
-
experimental: { disableExperimentalWarning: true },
|
|
12
|
-
});
|
|
13
|
-
baseUrl = `http://localhost:${worker.port}`;
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
afterAll(async () => {
|
|
17
|
-
await worker.stop();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should return healthy status from /health', async () => {
|
|
21
|
-
const response = await fetch(`${baseUrl}/health`);
|
|
22
|
-
expect(response.status).toBe(200);
|
|
23
|
-
|
|
24
|
-
const data = await response.json();
|
|
25
|
-
expect(data).toHaveProperty('status', 'healthy');
|
|
26
|
-
expect(data).toHaveProperty('timestamp');
|
|
27
|
-
expect(data).toHaveProperty('transport');
|
|
28
|
-
expect(data.transport).toHaveProperty('sse', '/sse');
|
|
29
|
-
expect(data.transport).toHaveProperty('streamableHttp', '/mcp');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should stream proofs via SSE and verify them', async () => {
|
|
33
|
-
const sessionId = `test-${Date.now()}`;
|
|
34
|
-
let proofReceived = false;
|
|
35
|
-
let proofData: any = null;
|
|
36
|
-
|
|
37
|
-
// Open SSE connection
|
|
38
|
-
const response = await fetch(`${baseUrl}/proofs?session=${sessionId}`);
|
|
39
|
-
expect(response.headers.get('content-type')).toMatch(/^text\/event-stream/);
|
|
40
|
-
expect(response.status).toBe(200);
|
|
41
|
-
|
|
42
|
-
// Create promise to track proof reception
|
|
43
|
-
const proofPromise = new Promise<void>((resolve, reject) => {
|
|
44
|
-
const timeout = setTimeout(() => {
|
|
45
|
-
reject(new Error('Timeout waiting for proof event'));
|
|
46
|
-
}, 10000);
|
|
47
|
-
|
|
48
|
-
if (!response.body) {
|
|
49
|
-
clearTimeout(timeout);
|
|
50
|
-
reject(new Error('No response body'));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const reader = response.body.getReader();
|
|
55
|
-
const decoder = new TextDecoder();
|
|
56
|
-
|
|
57
|
-
const readChunk = async () => {
|
|
58
|
-
try {
|
|
59
|
-
const { done, value } = await reader.read();
|
|
60
|
-
if (done) {
|
|
61
|
-
clearTimeout(timeout);
|
|
62
|
-
reject(new Error('Stream ended without receiving proof'));
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const chunk = decoder.decode(value);
|
|
67
|
-
const lines = chunk.split('\n');
|
|
68
|
-
|
|
69
|
-
for (const line of lines) {
|
|
70
|
-
if (line.startsWith('data: ')) {
|
|
71
|
-
const data = line.slice(6);
|
|
72
|
-
try {
|
|
73
|
-
proofData = JSON.parse(data);
|
|
74
|
-
proofReceived = true;
|
|
75
|
-
clearTimeout(timeout);
|
|
76
|
-
reader.cancel();
|
|
77
|
-
resolve();
|
|
78
|
-
return;
|
|
79
|
-
} catch (e) {
|
|
80
|
-
// Not valid JSON, continue reading
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Continue reading
|
|
86
|
-
readChunk();
|
|
87
|
-
} catch (error) {
|
|
88
|
-
clearTimeout(timeout);
|
|
89
|
-
reject(error);
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
readChunk();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Call the greet tool to generate a proof
|
|
97
|
-
const toolResponse = await fetch(`${baseUrl}/mcp`, {
|
|
98
|
-
method: 'POST',
|
|
99
|
-
headers: {
|
|
100
|
-
'Content-Type': 'application/json',
|
|
101
|
-
'mcp-session-id': sessionId,
|
|
102
|
-
},
|
|
103
|
-
body: JSON.stringify({
|
|
104
|
-
jsonrpc: '2.0',
|
|
105
|
-
id: 1,
|
|
106
|
-
method: 'tools/call',
|
|
107
|
-
params: {
|
|
108
|
-
name: 'greet',
|
|
109
|
-
arguments: {
|
|
110
|
-
name: 'Test User',
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
}),
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
expect(toolResponse.status).toBe(200);
|
|
117
|
-
const toolResult = await toolResponse.json();
|
|
118
|
-
expect(toolResult).toHaveProperty('result');
|
|
119
|
-
|
|
120
|
-
// Wait for proof to arrive via SSE
|
|
121
|
-
await proofPromise;
|
|
122
|
-
expect(proofReceived).toBe(true);
|
|
123
|
-
expect(proofData).toBeDefined();
|
|
124
|
-
expect(proofData).toHaveProperty('proof');
|
|
125
|
-
expect(proofData).toHaveProperty('data');
|
|
126
|
-
|
|
127
|
-
// Verify the proof
|
|
128
|
-
const verifyResponse = await fetch(`${baseUrl}/verify`, {
|
|
129
|
-
method: 'POST',
|
|
130
|
-
headers: {
|
|
131
|
-
'Content-Type': 'application/json',
|
|
132
|
-
},
|
|
133
|
-
body: JSON.stringify({
|
|
134
|
-
data: proofData.data,
|
|
135
|
-
proof: proofData.proof,
|
|
136
|
-
}),
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
expect(verifyResponse.status).toBe(200);
|
|
140
|
-
const verifyResult = await verifyResponse.json();
|
|
141
|
-
expect(verifyResult).toHaveProperty('valid', true);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('should expose identity via well-known endpoint', async () => {
|
|
145
|
-
const response = await fetch(`${baseUrl}/.well-known/mcp-identity`);
|
|
146
|
-
expect(response.status).toBe(200);
|
|
147
|
-
|
|
148
|
-
const identity = await response.json();
|
|
149
|
-
expect(identity).toHaveProperty('did');
|
|
150
|
-
expect(identity).toHaveProperty('publicKey');
|
|
151
|
-
expect(identity).toHaveProperty('serviceName', 'test-cloudflare');
|
|
152
|
-
expect(identity).toHaveProperty('serviceEndpoint');
|
|
153
|
-
expect(identity).toHaveProperty('timestamp');
|
|
154
|
-
expect(identity.did).toMatch(/^did:(key|web):/);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('should expose W3C DID document at /.well-known/did.json', async () => {
|
|
158
|
-
const response = await fetch(`${baseUrl}/.well-known/did.json`);
|
|
159
|
-
expect(response.status).toBe(200);
|
|
160
|
-
expect(response.headers.get('content-type')).toBe('application/did+json');
|
|
161
|
-
expect(response.headers.get('access-control-allow-origin')).toBe('*');
|
|
162
|
-
expect(response.headers.get('vary')).toBe('Origin');
|
|
163
|
-
|
|
164
|
-
const didDoc = await response.json();
|
|
165
|
-
expect(didDoc).toHaveProperty('@context');
|
|
166
|
-
expect(didDoc).toHaveProperty('id');
|
|
167
|
-
expect(didDoc.id).toMatch(/^did:key:z/);
|
|
168
|
-
expect(didDoc).toHaveProperty('verificationMethod');
|
|
169
|
-
expect(Array.isArray(didDoc.verificationMethod)).toBe(true);
|
|
170
|
-
expect(didDoc.verificationMethod.length).toBeGreaterThan(0);
|
|
171
|
-
|
|
172
|
-
const vm = didDoc.verificationMethod[0];
|
|
173
|
-
expect(vm).toHaveProperty('id');
|
|
174
|
-
expect(vm).toHaveProperty('type', 'Ed25519VerificationKey2020');
|
|
175
|
-
expect(vm).toHaveProperty('controller');
|
|
176
|
-
expect(vm).toHaveProperty('publicKeyBase64');
|
|
177
|
-
|
|
178
|
-
expect(didDoc).toHaveProperty('authentication');
|
|
179
|
-
expect(Array.isArray(didDoc.authentication)).toBe(true);
|
|
180
|
-
expect(didDoc).toHaveProperty('assertionMethod');
|
|
181
|
-
expect(Array.isArray(didDoc.assertionMethod)).toBe(true);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('should expose MCP-I capabilities at /.well-known/agent.json', async () => {
|
|
185
|
-
const response = await fetch(`${baseUrl}/.well-known/agent.json`);
|
|
186
|
-
expect(response.status).toBe(200);
|
|
187
|
-
expect(response.headers.get('content-type')).toBe('application/json');
|
|
188
|
-
expect(response.headers.get('access-control-allow-origin')).toBe('*');
|
|
189
|
-
expect(response.headers.get('vary')).toBe('Origin');
|
|
190
|
-
|
|
191
|
-
const agentDoc = await response.json();
|
|
192
|
-
expect(agentDoc).toHaveProperty('id');
|
|
193
|
-
expect(agentDoc.id).toMatch(/^did:key:z/);
|
|
194
|
-
expect(agentDoc).toHaveProperty('capabilities');
|
|
195
|
-
expect(agentDoc.capabilities).toHaveProperty('mcp-i');
|
|
196
|
-
expect(Array.isArray(agentDoc.capabilities['mcp-i'])).toBe(true);
|
|
197
|
-
expect(agentDoc.capabilities['mcp-i']).toContain('handshake');
|
|
198
|
-
expect(agentDoc.capabilities['mcp-i']).toContain('signing');
|
|
199
|
-
expect(agentDoc.capabilities['mcp-i']).toContain('verification');
|
|
200
|
-
|
|
201
|
-
expect(agentDoc).toHaveProperty('metadata');
|
|
202
|
-
expect(agentDoc.metadata).toHaveProperty('name', 'test-cloudflare');
|
|
203
|
-
expect(agentDoc.metadata).toHaveProperty('serviceEndpoint');
|
|
204
|
-
});
|
|
205
|
-
});
|