cloudflare-mcp-smart-proxy 1.3.0 → 1.3.1
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/index.js +5 -3
- package/package.json +12 -2
- package/src/cloud-client.js +4 -2
- package/src/device-identity.js +4 -3
- package/src/router.js +43 -12
package/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
ListPromptsRequestSchema,
|
|
14
14
|
GetPromptRequestSchema
|
|
15
15
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
16
|
-
import { SmartRouter } from './src/router.js';
|
|
16
|
+
import { sanitizeProxyError, SmartRouter } from './src/router.js';
|
|
17
17
|
import { LocalToolExecutor } from './src/local-tools.js';
|
|
18
18
|
import { ConnectorBridge } from './src/connector-bridge.js';
|
|
19
19
|
|
|
@@ -156,7 +156,7 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
|
156
156
|
|
|
157
157
|
return result.result || { prompts: [] };
|
|
158
158
|
} catch (error) {
|
|
159
|
-
console.error('Error listing prompts:', error);
|
|
159
|
+
console.error('Error listing prompts:', { error: sanitizeProxyError(error) });
|
|
160
160
|
return { prompts: [] };
|
|
161
161
|
}
|
|
162
162
|
});
|
|
@@ -198,7 +198,9 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
198
198
|
messages: []
|
|
199
199
|
};
|
|
200
200
|
} catch (error) {
|
|
201
|
-
console.error(
|
|
201
|
+
console.error('Error getting prompt:', {
|
|
202
|
+
error: sanitizeProxyError(error, request.params?.arguments || {})
|
|
203
|
+
});
|
|
202
204
|
return {
|
|
203
205
|
description: '',
|
|
204
206
|
messages: []
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudflare-mcp-smart-proxy",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Smart proxy for Cloudflare MCP - routes tools to cloud or local execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.33.0",
|
|
@@ -24,7 +24,17 @@
|
|
|
24
24
|
"author": "",
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.30.0"
|
|
28
|
+
},
|
|
29
|
+
"pnpm": {
|
|
30
|
+
"overrides": {
|
|
31
|
+
"@hono/node-server": "2.0.12",
|
|
32
|
+
"ajv": "8.20.0",
|
|
33
|
+
"body-parser": "2.3.0",
|
|
34
|
+
"fast-uri": "3.1.4",
|
|
35
|
+
"path-to-regexp": "8.4.0",
|
|
36
|
+
"qs": "6.15.3"
|
|
37
|
+
}
|
|
28
38
|
},
|
|
29
39
|
"engines": {
|
|
30
40
|
"node": ">=18.0.0"
|
package/src/cloud-client.js
CHANGED
|
@@ -27,14 +27,16 @@ export class CloudClient {
|
|
|
27
27
|
|
|
28
28
|
async request(path, { method = 'GET', body, query, idempotencyKey } = {}) {
|
|
29
29
|
const serializedBody = body == null ? '' : JSON.stringify(body);
|
|
30
|
+
const requestUrl = new URL(this.buildUrl(path, query));
|
|
30
31
|
const signedHeaders = this.deviceIdentity
|
|
31
32
|
? await this.deviceIdentity.buildSignedHeaders({
|
|
32
33
|
method,
|
|
33
|
-
pathname:
|
|
34
|
+
pathname: requestUrl.pathname,
|
|
35
|
+
search: requestUrl.search,
|
|
34
36
|
body: serializedBody
|
|
35
37
|
})
|
|
36
38
|
: {};
|
|
37
|
-
const response = await fetch(
|
|
39
|
+
const response = await fetch(requestUrl, {
|
|
38
40
|
method,
|
|
39
41
|
headers: {
|
|
40
42
|
'Authorization': `Bearer ${this.cloudApiKey}`,
|
package/src/device-identity.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import { webcrypto } from 'crypto';
|
|
5
5
|
|
|
6
6
|
const subtle = webcrypto.subtle;
|
|
7
|
-
const DEVICE_SIGNATURE_VERSION = '
|
|
7
|
+
const DEVICE_SIGNATURE_VERSION = 'device_sig_v2';
|
|
8
8
|
|
|
9
9
|
function bytesToBase64(bytes) {
|
|
10
10
|
return Buffer.from(bytes).toString('base64');
|
|
@@ -59,7 +59,7 @@ export class DeviceIdentity {
|
|
|
59
59
|
return this.record;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
async buildSignedHeaders({ method, pathname, body = '' }) {
|
|
62
|
+
async buildSignedHeaders({ method, pathname, search = '', body = '' }) {
|
|
63
63
|
const record = await this.loadOrCreate();
|
|
64
64
|
const timestamp = String(Date.now());
|
|
65
65
|
const nonce = crypto.randomUUID();
|
|
@@ -67,7 +67,7 @@ export class DeviceIdentity {
|
|
|
67
67
|
const canonical = [
|
|
68
68
|
DEVICE_SIGNATURE_VERSION,
|
|
69
69
|
String(method || 'GET').toUpperCase(),
|
|
70
|
-
pathname || '/'
|
|
70
|
+
`${pathname || '/'}${search || ''}`,
|
|
71
71
|
bodyHash,
|
|
72
72
|
timestamp,
|
|
73
73
|
nonce
|
|
@@ -79,6 +79,7 @@ export class DeviceIdentity {
|
|
|
79
79
|
);
|
|
80
80
|
return {
|
|
81
81
|
'X-CloudMCP-Device-ID': record.deviceId,
|
|
82
|
+
'X-CloudMCP-Device-Signature-Version': DEVICE_SIGNATURE_VERSION,
|
|
82
83
|
'X-CloudMCP-Device-Timestamp': timestamp,
|
|
83
84
|
'X-CloudMCP-Device-Nonce': nonce,
|
|
84
85
|
'X-CloudMCP-Device-Signature': bytesToBase64(signature)
|
package/src/router.js
CHANGED
|
@@ -2,6 +2,41 @@
|
|
|
2
2
|
* Smart Router - 智能路由工具请求到云端或本地
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
function collectArgumentStrings(value, output = new Set(), seen = new WeakSet()) {
|
|
6
|
+
if (typeof value === 'string') {
|
|
7
|
+
const normalized = value.trim();
|
|
8
|
+
if (normalized.length >= 2) output.add(normalized);
|
|
9
|
+
return output;
|
|
10
|
+
}
|
|
11
|
+
if (value == null || typeof value !== 'object' || seen.has(value)) return output;
|
|
12
|
+
seen.add(value);
|
|
13
|
+
for (const item of Array.isArray(value) ? value : Object.values(value)) {
|
|
14
|
+
collectArgumentStrings(item, output, seen);
|
|
15
|
+
}
|
|
16
|
+
return output;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function sanitizeProxyError(error, params = {}) {
|
|
20
|
+
let message = typeof error === 'string'
|
|
21
|
+
? error
|
|
22
|
+
: (error?.message || JSON.stringify(error || 'Unknown error'));
|
|
23
|
+
const values = Array.from(collectArgumentStrings(params))
|
|
24
|
+
.sort((left, right) => right.length - left.length);
|
|
25
|
+
for (const value of values) {
|
|
26
|
+
const variants = new Set([value]);
|
|
27
|
+
try {
|
|
28
|
+
variants.add(encodeURIComponent(value));
|
|
29
|
+
} catch {}
|
|
30
|
+
for (const variant of variants) {
|
|
31
|
+
message = message.split(variant).join('[REDACTED_PARAM]');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return String(message)
|
|
35
|
+
.replace(/Bearer\s+[A-Za-z0-9._~+\/-]+=*/gi, 'Bearer [REDACTED]')
|
|
36
|
+
.replace(/mcp_[A-Za-z0-9]{16,}/g, '[REDACTED]')
|
|
37
|
+
.slice(0, 512);
|
|
38
|
+
}
|
|
39
|
+
|
|
5
40
|
export class SmartRouter {
|
|
6
41
|
constructor(cloudUrl, cloudApiKey, localTools, workspaceRoot = null, deviceIdentity = null) {
|
|
7
42
|
this.cloudUrl = cloudUrl.replace(/\/$/, ''); // 移除尾部斜杠
|
|
@@ -195,15 +230,13 @@ export class SmartRouter {
|
|
|
195
230
|
errorMessage = errorMessage.message || JSON.stringify(errorMessage, null, 2);
|
|
196
231
|
}
|
|
197
232
|
|
|
198
|
-
|
|
233
|
+
const safeErrorMessage = sanitizeProxyError(errorMessage, params);
|
|
199
234
|
console.error(`[SmartRouter] Cloud tool error for ${toolName}:`, {
|
|
200
235
|
code: result.error.code,
|
|
201
|
-
message:
|
|
202
|
-
data: result.error.data,
|
|
203
|
-
fullError: result.error
|
|
236
|
+
message: safeErrorMessage
|
|
204
237
|
});
|
|
205
238
|
|
|
206
|
-
throw new Error(
|
|
239
|
+
throw new Error(safeErrorMessage);
|
|
207
240
|
}
|
|
208
241
|
|
|
209
242
|
// 提取结果内容
|
|
@@ -229,19 +262,17 @@ export class SmartRouter {
|
|
|
229
262
|
return result;
|
|
230
263
|
}
|
|
231
264
|
} catch (error) {
|
|
232
|
-
|
|
265
|
+
const safeErrorMessage = sanitizeProxyError(error, params);
|
|
233
266
|
console.error(`[SmartRouter] Error calling cloud tool ${toolName}:`, {
|
|
234
|
-
error:
|
|
235
|
-
stack: error.stack,
|
|
236
|
-
params: params
|
|
267
|
+
error: safeErrorMessage
|
|
237
268
|
});
|
|
238
269
|
|
|
239
270
|
// 如果错误信息已经包含 "Cloud tool call failed",直接抛出
|
|
240
271
|
// 否则添加前缀
|
|
241
|
-
if (
|
|
242
|
-
throw
|
|
272
|
+
if (safeErrorMessage.includes('Cloud tool call failed')) {
|
|
273
|
+
throw new Error(safeErrorMessage);
|
|
243
274
|
}
|
|
244
|
-
throw new Error(`Cloud tool call failed: ${
|
|
275
|
+
throw new Error(`Cloud tool call failed: ${safeErrorMessage}`);
|
|
245
276
|
}
|
|
246
277
|
}
|
|
247
278
|
|