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 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(`Error getting prompt:`, 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.0",
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.0.0"
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"
@@ -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: new URL(this.buildUrl(path, query)).pathname,
34
+ pathname: requestUrl.pathname,
35
+ search: requestUrl.search,
34
36
  body: serializedBody
35
37
  })
36
38
  : {};
37
- const response = await fetch(this.buildUrl(path, query), {
39
+ const response = await fetch(requestUrl, {
38
40
  method,
39
41
  headers: {
40
42
  'Authorization': `Bearer ${this.cloudApiKey}`,
@@ -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 = 'device_sig_v1';
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: result.error.message,
202
- data: result.error.data,
203
- fullError: result.error
236
+ message: safeErrorMessage
204
237
  });
205
238
 
206
- throw new Error(errorMessage);
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: error.message,
235
- stack: error.stack,
236
- params: params
267
+ error: safeErrorMessage
237
268
  });
238
269
 
239
270
  // 如果错误信息已经包含 "Cloud tool call failed",直接抛出
240
271
  // 否则添加前缀
241
- if (error.message && error.message.includes('Cloud tool call failed')) {
242
- throw error;
272
+ if (safeErrorMessage.includes('Cloud tool call failed')) {
273
+ throw new Error(safeErrorMessage);
243
274
  }
244
- throw new Error(`Cloud tool call failed: ${error.message}`);
275
+ throw new Error(`Cloud tool call failed: ${safeErrorMessage}`);
245
276
  }
246
277
  }
247
278