@kya-os/mcp-i-cloudflare 1.3.6 → 1.3.8
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/adapter.d.ts +3 -3
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +171 -150
- package/dist/adapter.js.map +1 -1
- package/dist/cache/kv-tool-protection-cache.d.ts.map +1 -1
- package/dist/cache/kv-tool-protection-cache.js +5 -0
- package/dist/cache/kv-tool-protection-cache.js.map +1 -1
- package/dist/config.d.ts +275 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +108 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -7
- package/dist/index.js.map +1 -1
- package/dist/proof-generator.d.ts +2 -0
- package/dist/proof-generator.d.ts.map +1 -1
- package/dist/proof-generator.js +1 -0
- package/dist/proof-generator.js.map +1 -1
- package/dist/providers/identity.d.ts +33 -0
- package/dist/providers/identity.d.ts.map +1 -0
- package/dist/providers/identity.js +62 -0
- package/dist/providers/identity.js.map +1 -0
- package/dist/providers/kv-identity.d.ts +42 -0
- package/dist/providers/kv-identity.d.ts.map +1 -0
- package/dist/providers/kv-identity.js +109 -0
- package/dist/providers/kv-identity.js.map +1 -0
- package/dist/providers/nonce-cache.d.ts +17 -0
- package/dist/providers/nonce-cache.d.ts.map +1 -0
- package/dist/providers/nonce-cache.js +58 -0
- package/dist/providers/nonce-cache.js.map +1 -0
- package/dist/runtime/index.d.ts +31 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +72 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/oauth-handler.d.ts.map +1 -1
- package/dist/runtime/oauth-handler.js +27 -21
- package/dist/runtime/oauth-handler.js.map +1 -1
- package/dist/runtime.d.ts +4 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +7 -2
- package/dist/runtime.js.map +1 -1
- package/package.json +9 -4
package/dist/adapter.js
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* This implementation avoids Node.js dependencies by implementing
|
|
9
9
|
* the MCP protocol directly.
|
|
10
10
|
*/
|
|
11
|
-
import { createCloudflareRuntime } from
|
|
12
|
-
import { WELL_KNOWN_CORS_HEADERS, MCP_CORS_HEADERS, PREFLIGHT_CORS_HEADERS } from
|
|
13
|
-
import { KVProofArchive } from
|
|
11
|
+
import { createCloudflareRuntime } from "./index";
|
|
12
|
+
import { WELL_KNOWN_CORS_HEADERS, MCP_CORS_HEADERS, PREFLIGHT_CORS_HEADERS, } from "@kya-os/mcp-i-core";
|
|
13
|
+
import { KVProofArchive } from "./storage/kv-proof-archive";
|
|
14
14
|
/**
|
|
15
15
|
* Lightweight MCP protocol implementation for Cloudflare Workers
|
|
16
16
|
*/
|
|
@@ -22,7 +22,7 @@ class CloudflareMCPServer {
|
|
|
22
22
|
constructor(runtime, serverInfo, tools = [], proofArchive) {
|
|
23
23
|
this.runtime = runtime;
|
|
24
24
|
this.serverInfo = serverInfo;
|
|
25
|
-
this.tools = new Map(tools.map(t => [t.name, t]));
|
|
25
|
+
this.tools = new Map(tools.map((t) => [t.name, t]));
|
|
26
26
|
this.proofArchive = proofArchive;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
@@ -32,34 +32,34 @@ class CloudflareMCPServer {
|
|
|
32
32
|
const { jsonrpc, id, method, params } = request;
|
|
33
33
|
try {
|
|
34
34
|
// Initialize/describe request
|
|
35
|
-
if (method ===
|
|
35
|
+
if (method === "initialize") {
|
|
36
36
|
return {
|
|
37
|
-
jsonrpc:
|
|
37
|
+
jsonrpc: "2.0",
|
|
38
38
|
id,
|
|
39
39
|
result: {
|
|
40
|
-
protocolVersion:
|
|
40
|
+
protocolVersion: "2025-06-18",
|
|
41
41
|
capabilities: {
|
|
42
|
-
tools: {}
|
|
42
|
+
tools: {},
|
|
43
43
|
},
|
|
44
|
-
serverInfo: this.serverInfo
|
|
45
|
-
}
|
|
44
|
+
serverInfo: this.serverInfo,
|
|
45
|
+
},
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
// List tools
|
|
49
|
-
if (method ===
|
|
50
|
-
const tools = Array.from(this.tools.values()).map(t => ({
|
|
49
|
+
if (method === "tools/list") {
|
|
50
|
+
const tools = Array.from(this.tools.values()).map((t) => ({
|
|
51
51
|
name: t.name,
|
|
52
52
|
description: t.description,
|
|
53
|
-
inputSchema: t.inputSchema
|
|
53
|
+
inputSchema: t.inputSchema,
|
|
54
54
|
}));
|
|
55
55
|
return {
|
|
56
|
-
jsonrpc:
|
|
56
|
+
jsonrpc: "2.0",
|
|
57
57
|
id,
|
|
58
|
-
result: { tools }
|
|
58
|
+
result: { tools },
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
// Call tool with identity/proof wrapping
|
|
62
|
-
if (method ===
|
|
62
|
+
if (method === "tools/call") {
|
|
63
63
|
const toolName = params.name;
|
|
64
64
|
const tool = this.tools.get(toolName);
|
|
65
65
|
if (!tool) {
|
|
@@ -73,11 +73,11 @@ class CloudflareMCPServer {
|
|
|
73
73
|
const randomSuffix = Math.random().toString(36).substring(2, 10);
|
|
74
74
|
session = {
|
|
75
75
|
id: `ephemeral-${timestamp}-${randomSuffix}`,
|
|
76
|
-
audience: params.audience ||
|
|
76
|
+
audience: params.audience || "mcp-client",
|
|
77
77
|
clientDid: params.clientDid,
|
|
78
78
|
agentDid: (await this.runtime.getIdentity()).did,
|
|
79
79
|
createdAt: timestamp,
|
|
80
|
-
expiresAt: timestamp +
|
|
80
|
+
expiresAt: timestamp + 30 * 60 * 1000, // 30 minutes
|
|
81
81
|
// Extract delegation token from MCP request if provided
|
|
82
82
|
delegationToken: params.delegationToken,
|
|
83
83
|
consentProof: params.consentProof,
|
|
@@ -99,7 +99,8 @@ class CloudflareMCPServer {
|
|
|
99
99
|
// Check if runtime has tool protection configured with requiredScopes
|
|
100
100
|
if (runtimeConfig?.delegation?.toolProtections?.[toolName]) {
|
|
101
101
|
const toolProtection = runtimeConfig.delegation.toolProtections[toolName];
|
|
102
|
-
if (toolProtection.requiredScopes &&
|
|
102
|
+
if (toolProtection.requiredScopes &&
|
|
103
|
+
toolProtection.requiredScopes.length > 0) {
|
|
103
104
|
// Use the first required scope as the scopeId (e.g., "files:read")
|
|
104
105
|
scopeId = toolProtection.requiredScopes[0];
|
|
105
106
|
}
|
|
@@ -125,26 +126,26 @@ class CloudflareMCPServer {
|
|
|
125
126
|
}
|
|
126
127
|
catch (error) {
|
|
127
128
|
// Log error but don't fail the request
|
|
128
|
-
console.error(
|
|
129
|
+
console.error("Failed to store proof:", error);
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
// Return result with detached proof (matching MCP-I pattern)
|
|
132
133
|
return {
|
|
133
|
-
jsonrpc:
|
|
134
|
+
jsonrpc: "2.0",
|
|
134
135
|
id,
|
|
135
136
|
result: {
|
|
136
137
|
result, // Tool execution result
|
|
137
|
-
proof // Detached proof with sessionId, audience, timestamp, etc.
|
|
138
|
-
}
|
|
138
|
+
proof, // Detached proof with sessionId, audience, timestamp, etc.
|
|
139
|
+
},
|
|
139
140
|
};
|
|
140
141
|
}
|
|
141
142
|
// Handle MCP-I specific handshake
|
|
142
|
-
if (method ===
|
|
143
|
+
if (method === "handshake") {
|
|
143
144
|
const handshakeResult = await this.runtime.handleHandshake(params || {});
|
|
144
145
|
return {
|
|
145
|
-
jsonrpc:
|
|
146
|
+
jsonrpc: "2.0",
|
|
146
147
|
id,
|
|
147
|
-
result: handshakeResult
|
|
148
|
+
result: handshakeResult,
|
|
148
149
|
};
|
|
149
150
|
}
|
|
150
151
|
// Unknown method
|
|
@@ -152,31 +153,44 @@ class CloudflareMCPServer {
|
|
|
152
153
|
}
|
|
153
154
|
catch (error) {
|
|
154
155
|
// Check if this is a DelegationRequiredError - include consent URL in response
|
|
155
|
-
if (error
|
|
156
|
+
if (error &&
|
|
157
|
+
(error.name === "DelegationRequiredError" ||
|
|
158
|
+
error.constructor?.name === "DelegationRequiredError")) {
|
|
156
159
|
return {
|
|
157
|
-
jsonrpc:
|
|
160
|
+
jsonrpc: "2.0",
|
|
158
161
|
id,
|
|
159
162
|
error: {
|
|
160
163
|
code: -32001, // Custom error code for authorization required
|
|
161
|
-
message: error.
|
|
164
|
+
message: error.consentUrl
|
|
165
|
+
? `${error.message || `Delegation required for tool "${error.toolName || "unknown"}"`}. <authorization_url>${error.consentUrl}</authorization_url>`
|
|
166
|
+
: error.message || `Delegation required for tool "${error.toolName || "unknown"}"`,
|
|
162
167
|
data: {
|
|
163
168
|
toolName: error.toolName,
|
|
164
169
|
requiredScopes: error.requiredScopes || [],
|
|
165
170
|
consentUrl: error.consentUrl,
|
|
171
|
+
resumeToken: error.resumeToken, // Include resume token for resumption
|
|
166
172
|
// MCP-I specific: provide authorization URL for client to display
|
|
167
173
|
authorizationUrl: error.consentUrl,
|
|
168
|
-
}
|
|
169
|
-
}
|
|
174
|
+
},
|
|
175
|
+
},
|
|
170
176
|
};
|
|
171
177
|
}
|
|
172
178
|
// Generic error handler
|
|
179
|
+
let errorMessage = "Internal error";
|
|
180
|
+
if (error) {
|
|
181
|
+
// Only use error message if it's an Error object with a message property
|
|
182
|
+
if (typeof error === 'object' && error instanceof Error && error.message) {
|
|
183
|
+
errorMessage = error.message;
|
|
184
|
+
}
|
|
185
|
+
// For non-Error objects (strings, null, etc.), use default "Internal error"
|
|
186
|
+
}
|
|
173
187
|
return {
|
|
174
|
-
jsonrpc:
|
|
188
|
+
jsonrpc: "2.0",
|
|
175
189
|
id,
|
|
176
190
|
error: {
|
|
177
191
|
code: -32603,
|
|
178
|
-
message:
|
|
179
|
-
}
|
|
192
|
+
message: errorMessage,
|
|
193
|
+
},
|
|
180
194
|
};
|
|
181
195
|
}
|
|
182
196
|
}
|
|
@@ -193,12 +207,14 @@ export function createMCPICloudflareAdapter(config) {
|
|
|
193
207
|
const runtime = createCloudflareRuntime(config);
|
|
194
208
|
// Server info
|
|
195
209
|
const serverInfo = config.serverInfo || {
|
|
196
|
-
name:
|
|
197
|
-
version:
|
|
210
|
+
name: "MCP-I Cloudflare Server",
|
|
211
|
+
version: "1.0.0",
|
|
198
212
|
};
|
|
199
213
|
// Initialize proof archive if PROOF_ARCHIVE KV is available
|
|
200
214
|
const env = config.env;
|
|
201
|
-
const proofArchive = env.PROOF_ARCHIVE
|
|
215
|
+
const proofArchive = env.PROOF_ARCHIVE
|
|
216
|
+
? new KVProofArchive(env.PROOF_ARCHIVE)
|
|
217
|
+
: undefined;
|
|
202
218
|
// Create lightweight MCP server
|
|
203
219
|
const server = new CloudflareMCPServer(runtime, serverInfo, config.tools || [], proofArchive);
|
|
204
220
|
// Return fetch handler
|
|
@@ -211,94 +227,97 @@ export function createMCPICloudflareAdapter(config) {
|
|
|
211
227
|
const url = new URL(request.url);
|
|
212
228
|
const identity = await runtime.getIdentity();
|
|
213
229
|
// Health check endpoint
|
|
214
|
-
if (url.pathname ===
|
|
230
|
+
if (url.pathname === "/health") {
|
|
215
231
|
return new Response(JSON.stringify({
|
|
216
|
-
status:
|
|
232
|
+
status: "healthy",
|
|
217
233
|
timestamp: new Date().toISOString(),
|
|
218
234
|
identity: {
|
|
219
|
-
did: identity.did
|
|
235
|
+
did: identity.did,
|
|
220
236
|
},
|
|
221
237
|
transport: {
|
|
222
|
-
http:
|
|
223
|
-
sse:
|
|
224
|
-
streamableHttp:
|
|
238
|
+
http: "/mcp",
|
|
239
|
+
sse: "/sse",
|
|
240
|
+
streamableHttp: "/mcp",
|
|
225
241
|
},
|
|
226
|
-
features: [
|
|
242
|
+
features: ["identity", "proof", "delegation", "audit"],
|
|
227
243
|
}), {
|
|
228
|
-
headers: {
|
|
244
|
+
headers: { "Content-Type": "application/json" },
|
|
229
245
|
});
|
|
230
246
|
}
|
|
231
247
|
// Well-known endpoints for identity verification
|
|
232
|
-
if (url.pathname.startsWith(
|
|
248
|
+
if (url.pathname.startsWith("/.well-known/")) {
|
|
233
249
|
const handler = runtime.createWellKnownHandler({
|
|
234
250
|
serviceName: serverInfo.name,
|
|
235
|
-
serviceEndpoint: request.headers.get(
|
|
251
|
+
serviceEndpoint: request.headers.get("host") || "localhost",
|
|
236
252
|
});
|
|
237
253
|
const result = await handler(url.pathname);
|
|
238
254
|
if (result) {
|
|
239
|
-
if (
|
|
255
|
+
// Check if it's a WellKnownResponse (has status, headers, body)
|
|
256
|
+
if ("status" in result && "headers" in result && "body" in result) {
|
|
240
257
|
return new Response(result.body, {
|
|
241
258
|
status: result.status,
|
|
242
259
|
headers: {
|
|
243
260
|
...result.headers,
|
|
244
|
-
...WELL_KNOWN_CORS_HEADERS
|
|
245
|
-
}
|
|
261
|
+
...WELL_KNOWN_CORS_HEADERS,
|
|
262
|
+
},
|
|
246
263
|
});
|
|
247
264
|
}
|
|
265
|
+
// Otherwise it's an MCPIdentity object, return as JSON
|
|
248
266
|
return new Response(JSON.stringify(result), {
|
|
267
|
+
status: 200,
|
|
249
268
|
headers: {
|
|
250
|
-
|
|
251
|
-
...WELL_KNOWN_CORS_HEADERS
|
|
252
|
-
}
|
|
269
|
+
"Content-Type": "application/json",
|
|
270
|
+
...WELL_KNOWN_CORS_HEADERS,
|
|
271
|
+
},
|
|
253
272
|
});
|
|
254
273
|
}
|
|
255
274
|
}
|
|
256
275
|
// Debug endpoint (development only)
|
|
257
|
-
if (url.pathname ===
|
|
276
|
+
if (url.pathname === "/debug" && config.environment !== "production") {
|
|
258
277
|
const debugHandler = runtime.createDebugEndpoint();
|
|
259
278
|
if (debugHandler) {
|
|
260
279
|
const result = await debugHandler();
|
|
261
280
|
return new Response(JSON.stringify(result), {
|
|
262
|
-
headers: {
|
|
281
|
+
headers: { "Content-Type": "application/json" },
|
|
263
282
|
});
|
|
264
283
|
}
|
|
265
284
|
}
|
|
266
285
|
// Main MCP protocol endpoint - supports both POST and SSE
|
|
267
|
-
if (url.pathname ===
|
|
286
|
+
if (url.pathname === "/mcp") {
|
|
268
287
|
// Handle SSE streaming (for Claude Desktop via mcp-remote)
|
|
269
|
-
if (request.method ===
|
|
270
|
-
const acceptHeader = request.headers.get(
|
|
271
|
-
if (acceptHeader.includes(
|
|
288
|
+
if (request.method === "GET") {
|
|
289
|
+
const acceptHeader = request.headers.get("Accept") || "";
|
|
290
|
+
if (acceptHeader.includes("text/event-stream")) {
|
|
272
291
|
return handleSSE(server, runtime);
|
|
273
292
|
}
|
|
274
293
|
// GET requests without SSE Accept header should return Method Not Allowed
|
|
275
294
|
// MCP protocol primarily uses POST for JSON-RPC
|
|
276
|
-
return new Response(
|
|
295
|
+
return new Response("Method Not Allowed: Use POST for JSON-RPC or GET with Accept: text/event-stream for SSE", {
|
|
277
296
|
status: 405,
|
|
278
297
|
headers: {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
...MCP_CORS_HEADERS
|
|
282
|
-
}
|
|
298
|
+
"Content-Type": "text/plain",
|
|
299
|
+
Allow: "POST, GET",
|
|
300
|
+
...MCP_CORS_HEADERS,
|
|
301
|
+
},
|
|
283
302
|
});
|
|
284
303
|
}
|
|
285
304
|
// Handle DELETE requests (session termination)
|
|
286
|
-
if (request.method ===
|
|
305
|
+
if (request.method === "DELETE") {
|
|
287
306
|
// MCP Inspector and clients may send DELETE to terminate sessions
|
|
288
307
|
// Return 200 OK with empty body (some environments don't support 204 well)
|
|
289
|
-
return new Response(
|
|
308
|
+
return new Response("", {
|
|
290
309
|
status: 200,
|
|
291
310
|
headers: {
|
|
292
|
-
|
|
293
|
-
...MCP_CORS_HEADERS
|
|
294
|
-
}
|
|
311
|
+
"Content-Length": "0",
|
|
312
|
+
...MCP_CORS_HEADERS,
|
|
313
|
+
},
|
|
295
314
|
});
|
|
296
315
|
}
|
|
297
316
|
// Handle standard POST (existing functionality)
|
|
298
|
-
if (request.method ===
|
|
299
|
-
const acceptHeader = request.headers.get(
|
|
317
|
+
if (request.method === "POST") {
|
|
318
|
+
const acceptHeader = request.headers.get("Accept") || "";
|
|
300
319
|
// Check if client wants SSE response
|
|
301
|
-
if (acceptHeader.includes(
|
|
320
|
+
if (acceptHeader.includes("text/event-stream")) {
|
|
302
321
|
// Return SSE stream for POST (Streamable HTTP mode)
|
|
303
322
|
return handleSSEPost(server, runtime, request);
|
|
304
323
|
}
|
|
@@ -310,100 +329,102 @@ export function createMCPICloudflareAdapter(config) {
|
|
|
310
329
|
const response = await server.handleRequest(jsonRpcRequest);
|
|
311
330
|
return new Response(JSON.stringify(response), {
|
|
312
331
|
headers: {
|
|
313
|
-
|
|
314
|
-
...MCP_CORS_HEADERS
|
|
315
|
-
}
|
|
332
|
+
"Content-Type": "application/json",
|
|
333
|
+
...MCP_CORS_HEADERS,
|
|
334
|
+
},
|
|
316
335
|
});
|
|
317
336
|
}
|
|
318
337
|
catch (error) {
|
|
319
338
|
return new Response(JSON.stringify({
|
|
320
|
-
jsonrpc:
|
|
339
|
+
jsonrpc: "2.0",
|
|
321
340
|
error: {
|
|
322
341
|
code: -32603,
|
|
323
|
-
message: error.message ||
|
|
324
|
-
}
|
|
342
|
+
message: error.message || "Internal error",
|
|
343
|
+
},
|
|
325
344
|
}), {
|
|
326
345
|
status: 500,
|
|
327
|
-
headers: {
|
|
346
|
+
headers: { "Content-Type": "application/json" },
|
|
328
347
|
});
|
|
329
348
|
}
|
|
330
349
|
}
|
|
331
350
|
}
|
|
332
351
|
// SSE endpoint for backward compatibility and Claude Desktop
|
|
333
|
-
if (url.pathname ===
|
|
334
|
-
if (request.method ===
|
|
352
|
+
if (url.pathname === "/sse") {
|
|
353
|
+
if (request.method === "GET") {
|
|
335
354
|
return handleSSE(server, runtime);
|
|
336
355
|
}
|
|
337
|
-
if (request.method ===
|
|
356
|
+
if (request.method === "POST") {
|
|
338
357
|
return handleSSEPost(server, runtime, request);
|
|
339
358
|
}
|
|
340
359
|
}
|
|
341
360
|
// Standalone verify endpoint for external verification
|
|
342
|
-
if (url.pathname ===
|
|
361
|
+
if (url.pathname === "/verify" && request.method === "POST") {
|
|
343
362
|
try {
|
|
344
|
-
const { data, proof } = await request.json();
|
|
363
|
+
const { data, proof } = (await request.json());
|
|
345
364
|
const isValid = await runtime.verifyProof(data, proof);
|
|
346
365
|
return new Response(JSON.stringify({
|
|
347
366
|
verified: isValid,
|
|
348
|
-
agent: isValid
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
367
|
+
agent: isValid
|
|
368
|
+
? {
|
|
369
|
+
did: proof.did,
|
|
370
|
+
timestamp: proof.timestamp,
|
|
371
|
+
sessionId: proof.sessionId,
|
|
372
|
+
}
|
|
373
|
+
: null,
|
|
353
374
|
}), {
|
|
354
|
-
headers: {
|
|
375
|
+
headers: { "Content-Type": "application/json" },
|
|
355
376
|
});
|
|
356
377
|
}
|
|
357
378
|
catch (error) {
|
|
358
379
|
return new Response(JSON.stringify({
|
|
359
|
-
error: error.message ||
|
|
380
|
+
error: error.message || "Verification failed",
|
|
360
381
|
}), {
|
|
361
382
|
status: 400,
|
|
362
|
-
headers: {
|
|
383
|
+
headers: { "Content-Type": "application/json" },
|
|
363
384
|
});
|
|
364
385
|
}
|
|
365
386
|
}
|
|
366
387
|
// Admin endpoints for viewing nonces and proofs
|
|
367
|
-
if (url.pathname.startsWith(
|
|
388
|
+
if (url.pathname.startsWith("/admin/")) {
|
|
368
389
|
// Only allow in development or if explicitly configured
|
|
369
|
-
if (config.environment ===
|
|
390
|
+
if (config.environment === "production") {
|
|
370
391
|
return new Response(JSON.stringify({
|
|
371
|
-
error:
|
|
392
|
+
error: "Admin endpoints disabled in production",
|
|
372
393
|
}), {
|
|
373
394
|
status: 403,
|
|
374
|
-
headers: {
|
|
395
|
+
headers: { "Content-Type": "application/json" },
|
|
375
396
|
});
|
|
376
397
|
}
|
|
377
398
|
const env = config.env;
|
|
378
399
|
// GET /admin/nonces - List active nonces
|
|
379
|
-
if (url.pathname ===
|
|
400
|
+
if (url.pathname === "/admin/nonces") {
|
|
380
401
|
try {
|
|
381
402
|
// Use KV list to get nonce keys
|
|
382
|
-
const noncesList = await env.NONCE_CACHE.list({ prefix:
|
|
403
|
+
const noncesList = await env.NONCE_CACHE.list({ prefix: "nonce:" });
|
|
383
404
|
const nonces = [];
|
|
384
405
|
for (const key of noncesList.keys) {
|
|
385
406
|
const value = await env.NONCE_CACHE.get(key.name);
|
|
386
407
|
if (value) {
|
|
387
408
|
nonces.push({
|
|
388
|
-
nonce: key.name.replace(
|
|
389
|
-
storedAt: key.metadata?.storedAt ||
|
|
390
|
-
expiresAt: key.expiration
|
|
409
|
+
nonce: key.name.replace("nonce:", ""),
|
|
410
|
+
storedAt: key.metadata?.storedAt || "unknown",
|
|
411
|
+
expiresAt: key.expiration,
|
|
391
412
|
});
|
|
392
413
|
}
|
|
393
414
|
}
|
|
394
415
|
return new Response(JSON.stringify({
|
|
395
416
|
total: nonces.length,
|
|
396
|
-
nonces
|
|
417
|
+
nonces,
|
|
397
418
|
}), {
|
|
398
|
-
headers: {
|
|
419
|
+
headers: { "Content-Type": "application/json" },
|
|
399
420
|
});
|
|
400
421
|
}
|
|
401
422
|
catch (error) {
|
|
402
423
|
return new Response(JSON.stringify({
|
|
403
|
-
error: error.message ||
|
|
424
|
+
error: error.message || "Failed to list nonces",
|
|
404
425
|
}), {
|
|
405
426
|
status: 500,
|
|
406
|
-
headers: {
|
|
427
|
+
headers: { "Content-Type": "application/json" },
|
|
407
428
|
});
|
|
408
429
|
}
|
|
409
430
|
}
|
|
@@ -413,18 +434,18 @@ export function createMCPICloudflareAdapter(config) {
|
|
|
413
434
|
: null;
|
|
414
435
|
if (!proofArchive) {
|
|
415
436
|
return new Response(JSON.stringify({
|
|
416
|
-
error:
|
|
437
|
+
error: "PROOF_ARCHIVE KV namespace not configured",
|
|
417
438
|
}), {
|
|
418
439
|
status: 503,
|
|
419
|
-
headers: {
|
|
440
|
+
headers: { "Content-Type": "application/json" },
|
|
420
441
|
});
|
|
421
442
|
}
|
|
422
443
|
// GET /admin/proofs - List proofs with optional filters
|
|
423
|
-
if (url.pathname ===
|
|
444
|
+
if (url.pathname === "/admin/proofs") {
|
|
424
445
|
try {
|
|
425
|
-
const sessionId = url.searchParams.get(
|
|
426
|
-
const did = url.searchParams.get(
|
|
427
|
-
const limit = parseInt(url.searchParams.get(
|
|
446
|
+
const sessionId = url.searchParams.get("sessionId");
|
|
447
|
+
const did = url.searchParams.get("did");
|
|
448
|
+
const limit = parseInt(url.searchParams.get("limit") || "50");
|
|
428
449
|
let proofs;
|
|
429
450
|
if (sessionId) {
|
|
430
451
|
proofs = await proofArchive.getBySession(sessionId, limit);
|
|
@@ -437,17 +458,17 @@ export function createMCPICloudflareAdapter(config) {
|
|
|
437
458
|
}
|
|
438
459
|
return new Response(JSON.stringify({
|
|
439
460
|
total: proofs.length,
|
|
440
|
-
proofs
|
|
461
|
+
proofs,
|
|
441
462
|
}), {
|
|
442
|
-
headers: {
|
|
463
|
+
headers: { "Content-Type": "application/json" },
|
|
443
464
|
});
|
|
444
465
|
}
|
|
445
466
|
catch (error) {
|
|
446
467
|
return new Response(JSON.stringify({
|
|
447
|
-
error: error.message ||
|
|
468
|
+
error: error.message || "Failed to list proofs",
|
|
448
469
|
}), {
|
|
449
470
|
status: 500,
|
|
450
|
-
headers: {
|
|
471
|
+
headers: { "Content-Type": "application/json" },
|
|
451
472
|
});
|
|
452
473
|
}
|
|
453
474
|
}
|
|
@@ -459,60 +480,60 @@ export function createMCPICloudflareAdapter(config) {
|
|
|
459
480
|
const proof = await proofArchive.get(proofId);
|
|
460
481
|
if (!proof) {
|
|
461
482
|
return new Response(JSON.stringify({
|
|
462
|
-
error:
|
|
483
|
+
error: "Proof not found",
|
|
463
484
|
}), {
|
|
464
485
|
status: 404,
|
|
465
|
-
headers: {
|
|
486
|
+
headers: { "Content-Type": "application/json" },
|
|
466
487
|
});
|
|
467
488
|
}
|
|
468
489
|
return new Response(JSON.stringify(proof), {
|
|
469
|
-
headers: {
|
|
490
|
+
headers: { "Content-Type": "application/json" },
|
|
470
491
|
});
|
|
471
492
|
}
|
|
472
493
|
catch (error) {
|
|
473
494
|
return new Response(JSON.stringify({
|
|
474
|
-
error: error.message ||
|
|
495
|
+
error: error.message || "Failed to get proof",
|
|
475
496
|
}), {
|
|
476
497
|
status: 500,
|
|
477
|
-
headers: {
|
|
498
|
+
headers: { "Content-Type": "application/json" },
|
|
478
499
|
});
|
|
479
500
|
}
|
|
480
501
|
}
|
|
481
502
|
// GET /admin/stats - Combined statistics
|
|
482
|
-
if (url.pathname ===
|
|
503
|
+
if (url.pathname === "/admin/stats") {
|
|
483
504
|
try {
|
|
484
505
|
const [noncesList, proofStats] = await Promise.all([
|
|
485
|
-
env.NONCE_CACHE.list({ prefix:
|
|
486
|
-
proofArchive.getStats()
|
|
506
|
+
env.NONCE_CACHE.list({ prefix: "nonce:" }),
|
|
507
|
+
proofArchive.getStats(),
|
|
487
508
|
]);
|
|
488
509
|
return new Response(JSON.stringify({
|
|
489
510
|
nonces: {
|
|
490
|
-
active: noncesList.keys.length
|
|
511
|
+
active: noncesList.keys.length,
|
|
491
512
|
},
|
|
492
513
|
proofs: proofStats,
|
|
493
|
-
timestamp: new Date().toISOString()
|
|
514
|
+
timestamp: new Date().toISOString(),
|
|
494
515
|
}), {
|
|
495
|
-
headers: {
|
|
516
|
+
headers: { "Content-Type": "application/json" },
|
|
496
517
|
});
|
|
497
518
|
}
|
|
498
519
|
catch (error) {
|
|
499
520
|
return new Response(JSON.stringify({
|
|
500
|
-
error: error.message ||
|
|
521
|
+
error: error.message || "Failed to get stats",
|
|
501
522
|
}), {
|
|
502
523
|
status: 500,
|
|
503
|
-
headers: {
|
|
524
|
+
headers: { "Content-Type": "application/json" },
|
|
504
525
|
});
|
|
505
526
|
}
|
|
506
527
|
}
|
|
507
528
|
}
|
|
508
529
|
// CORS preflight
|
|
509
|
-
if (request.method ===
|
|
530
|
+
if (request.method === "OPTIONS") {
|
|
510
531
|
return new Response(null, {
|
|
511
|
-
headers: PREFLIGHT_CORS_HEADERS
|
|
532
|
+
headers: PREFLIGHT_CORS_HEADERS,
|
|
512
533
|
});
|
|
513
534
|
}
|
|
514
|
-
return new Response(
|
|
515
|
-
}
|
|
535
|
+
return new Response("Not found", { status: 404 });
|
|
536
|
+
},
|
|
516
537
|
};
|
|
517
538
|
}
|
|
518
539
|
/**
|
|
@@ -525,14 +546,14 @@ async function handleSSE(server, runtime) {
|
|
|
525
546
|
const encoder = new TextEncoder();
|
|
526
547
|
// Return a simple SSE ping response
|
|
527
548
|
// This establishes the SSE connection but doesn't try to keep it alive
|
|
528
|
-
const sseData =
|
|
549
|
+
const sseData = ": ping\n\n";
|
|
529
550
|
return new Response(encoder.encode(sseData), {
|
|
530
551
|
headers: {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
...MCP_CORS_HEADERS
|
|
535
|
-
}
|
|
552
|
+
"Content-Type": "text/event-stream",
|
|
553
|
+
"Cache-Control": "no-cache",
|
|
554
|
+
"X-Content-Type-Options": "nosniff",
|
|
555
|
+
...MCP_CORS_HEADERS,
|
|
556
|
+
},
|
|
536
557
|
});
|
|
537
558
|
}
|
|
538
559
|
/**
|
|
@@ -553,30 +574,30 @@ async function handleSSEPost(server, runtime, request) {
|
|
|
553
574
|
// Return SSE response directly without TransformStream
|
|
554
575
|
return new Response(encoder.encode(eventData), {
|
|
555
576
|
headers: {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
...MCP_CORS_HEADERS
|
|
560
|
-
}
|
|
577
|
+
"Content-Type": "text/event-stream",
|
|
578
|
+
"Cache-Control": "no-cache",
|
|
579
|
+
"X-Content-Type-Options": "nosniff",
|
|
580
|
+
...MCP_CORS_HEADERS,
|
|
581
|
+
},
|
|
561
582
|
});
|
|
562
583
|
}
|
|
563
584
|
catch (error) {
|
|
564
585
|
const errorResponse = {
|
|
565
|
-
jsonrpc:
|
|
586
|
+
jsonrpc: "2.0",
|
|
566
587
|
error: {
|
|
567
588
|
code: -32603,
|
|
568
|
-
message: error.message ||
|
|
569
|
-
}
|
|
589
|
+
message: error.message || "Internal error",
|
|
590
|
+
},
|
|
570
591
|
};
|
|
571
592
|
const eventData = `data: ${JSON.stringify(errorResponse)}\n\n`;
|
|
572
593
|
// Return error as SSE event (status 200 per SSE spec)
|
|
573
594
|
return new Response(encoder.encode(eventData), {
|
|
574
595
|
status: 200, // SSE always returns 200, even for errors
|
|
575
596
|
headers: {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
...MCP_CORS_HEADERS
|
|
579
|
-
}
|
|
597
|
+
"Content-Type": "text/event-stream",
|
|
598
|
+
"Cache-Control": "no-cache",
|
|
599
|
+
...MCP_CORS_HEADERS,
|
|
600
|
+
},
|
|
580
601
|
});
|
|
581
602
|
}
|
|
582
603
|
}
|