cloudflare-mcp-smart-proxy 1.2.0 → 1.3.0

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.
@@ -10,8 +10,9 @@ import { SymbolTools } from './tools/symbol-tools.js';
10
10
  import { Diagnostics } from './tools/diagnostics.js';
11
11
 
12
12
  export class LocalToolExecutor {
13
- constructor(workspaceRoot) {
13
+ constructor(workspaceRoot, connectorBridge = null) {
14
14
  this.workspaceRoot = workspaceRoot;
15
+ this.connectorBridge = connectorBridge;
15
16
  }
16
17
 
17
18
  /**
@@ -55,6 +56,26 @@ export class LocalToolExecutor {
55
56
 
56
57
  case 'get_diagnostics':
57
58
  return await Diagnostics.getDiagnostics(params, this.workspaceRoot);
59
+
60
+ case 'connector_bridge_status':
61
+ if (!this.connectorBridge) throw new Error('Connector bridge is not configured');
62
+ return this.connectorBridge.getBridgeStatus();
63
+
64
+ case 'connector_sync_profile':
65
+ if (!this.connectorBridge) throw new Error('Connector bridge is not configured');
66
+ return await this.connectorBridge.syncProfile();
67
+
68
+ case 'connector_report_status':
69
+ if (!this.connectorBridge) throw new Error('Connector bridge is not configured');
70
+ return await this.connectorBridge.reportStatus(params || {});
71
+
72
+ case 'connector_discover_project_probe':
73
+ if (!this.connectorBridge) throw new Error('Connector bridge is not configured');
74
+ return await this.connectorBridge.discoverProjectProbe();
75
+
76
+ case 'connector_report_project_probe':
77
+ if (!this.connectorBridge) throw new Error('Connector bridge is not configured');
78
+ return await this.connectorBridge.reportProjectProbe(params || {});
58
79
 
59
80
  default:
60
81
  throw new Error(`Unknown local tool: ${toolName}`);
@@ -314,8 +335,69 @@ export class LocalToolExecutor {
314
335
  },
315
336
  required: ['file_path']
316
337
  }
338
+ },
339
+ {
340
+ name: 'connector_bridge_status',
341
+ description: 'Get current connector bridge state, sync metadata, and workspace binding status',
342
+ inputSchema: {
343
+ type: 'object',
344
+ properties: {}
345
+ }
346
+ },
347
+ {
348
+ name: 'connector_sync_profile',
349
+ description: 'Pull the latest install plan and recent status reports for the configured client profile',
350
+ inputSchema: {
351
+ type: 'object',
352
+ properties: {}
353
+ }
354
+ },
355
+ {
356
+ name: 'connector_report_status',
357
+ description: 'Report local connector apply status back to CloudMCP',
358
+ inputSchema: {
359
+ type: 'object',
360
+ properties: {
361
+ status: {
362
+ type: 'string',
363
+ description: 'Overall status, for example succeeded, degraded, or failed'
364
+ },
365
+ summary: {
366
+ type: 'object',
367
+ description: 'High level summary of local apply results'
368
+ },
369
+ operationResults: {
370
+ type: 'array',
371
+ description: 'Per-operation execution results'
372
+ },
373
+ issues: {
374
+ type: 'array',
375
+ description: 'Reported install or runtime issues'
376
+ }
377
+ }
378
+ }
379
+ },
380
+ {
381
+ name: 'connector_discover_project_probe',
382
+ description: 'Discover the local workspace skeleton and build a project_probe candidate payload',
383
+ inputSchema: {
384
+ type: 'object',
385
+ properties: {}
386
+ }
387
+ },
388
+ {
389
+ name: 'connector_report_project_probe',
390
+ description: 'Report the local workspace project probe to CloudMCP and optionally auto-generate a draft context pack',
391
+ inputSchema: {
392
+ type: 'object',
393
+ properties: {
394
+ autoGenerateContextPack: {
395
+ type: 'boolean',
396
+ description: 'Whether CloudMCP should auto-generate a draft context pack from this probe'
397
+ }
398
+ }
399
+ }
317
400
  }
318
401
  ];
319
402
  }
320
403
  }
321
-
@@ -0,0 +1,137 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
4
+ async function pathExists(target) {
5
+ try {
6
+ await fs.access(target);
7
+ return true;
8
+ } catch {
9
+ return false;
10
+ }
11
+ }
12
+
13
+ async function safeReadDir(target) {
14
+ try {
15
+ return await fs.readdir(target, { withFileTypes: true });
16
+ } catch {
17
+ return [];
18
+ }
19
+ }
20
+
21
+ function inferLanguages(fileNames = []) {
22
+ const set = new Set();
23
+ if (fileNames.some((name) => name.endsWith('.js') || name.endsWith('.mjs') || name.endsWith('.cjs'))) set.add('javascript');
24
+ if (fileNames.some((name) => name.endsWith('.ts') || name.endsWith('.tsx'))) set.add('typescript');
25
+ if (fileNames.some((name) => name.endsWith('.py'))) set.add('python');
26
+ if (fileNames.some((name) => name.endsWith('.go'))) set.add('go');
27
+ if (fileNames.some((name) => name.endsWith('.rs'))) set.add('rust');
28
+ if (fileNames.some((name) => name.endsWith('.java'))) set.add('java');
29
+ if (fileNames.some((name) => name.endsWith('.html'))) set.add('html');
30
+ if (fileNames.some((name) => name.endsWith('.css'))) set.add('css');
31
+ return Array.from(set);
32
+ }
33
+
34
+ function inferFrameworkHints(fileNames = [], dirs = []) {
35
+ const set = new Set();
36
+ if (fileNames.includes('wrangler.toml')) set.add('cloudflare_workers');
37
+ if (fileNames.includes('package.json')) set.add('node_workspace');
38
+ if (fileNames.includes('pnpm-workspace.yaml') || dirs.includes('packages') || dirs.includes('apps')) set.add('monorepo');
39
+ if (fileNames.includes('next.config.js') || fileNames.includes('next.config.mjs')) set.add('nextjs');
40
+ if (fileNames.includes('vite.config.js') || fileNames.includes('vite.config.ts')) set.add('vite');
41
+ if (fileNames.includes('go.mod')) set.add('go_module');
42
+ if (fileNames.includes('pyproject.toml')) set.add('python_project');
43
+ return Array.from(set);
44
+ }
45
+
46
+ function inferPackageManager(fileNames = []) {
47
+ if (fileNames.includes('pnpm-lock.yaml')) return 'pnpm';
48
+ if (fileNames.includes('yarn.lock')) return 'yarn';
49
+ if (fileNames.includes('package-lock.json')) return 'npm';
50
+ if (fileNames.includes('bun.lockb') || fileNames.includes('bun.lock')) return 'bun';
51
+ return '';
52
+ }
53
+
54
+ function buildWorkspaceLayout(dirs = []) {
55
+ const preferred = ['apps', 'packages', 'src', 'tests', 'docs', 'scripts'];
56
+ const found = preferred.filter((name) => dirs.includes(name));
57
+ return found.join(' + ');
58
+ }
59
+
60
+ export async function discoverProjectProbe({
61
+ workspaceRoot,
62
+ workspaceId,
63
+ clientProfileId = null,
64
+ connectorId = null,
65
+ connectorType = null,
66
+ generatedBy = 'connector_scan'
67
+ }) {
68
+ const repoRoot = path.resolve(workspaceRoot || process.cwd());
69
+ const repoName = path.basename(repoRoot);
70
+ const topLevel = await safeReadDir(repoRoot);
71
+ const dirNames = topLevel.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
72
+ const fileNames = topLevel.filter((entry) => entry.isFile()).map((entry) => entry.name);
73
+
74
+ const keyFiles = [
75
+ 'wrangler.toml',
76
+ 'package.json',
77
+ 'pnpm-workspace.yaml',
78
+ 'README.md',
79
+ 'src/worker.js',
80
+ 'src/admin-page.html',
81
+ 'docs/CLOUDMCP_CURRENT_DESIGN_FRAMEWORK_AND_DATAFLOW_2026-05-07.md'
82
+ ];
83
+ const docCandidates = [
84
+ 'README.md',
85
+ 'docs/CLOUDMCP_CURRENT_DESIGN_FRAMEWORK_AND_DATAFLOW_2026-05-07.md',
86
+ 'docs/CLOUDMCP_INTERNAL_CAPABILITY_BRIDGE_STANDARD_2026-05-06.md',
87
+ 'docs/CLOUDMCP_EXTERNAL_TOOL_SURFACE_STANDARD_2026-05-06.md'
88
+ ];
89
+
90
+ const resolvedKeyFiles = [];
91
+ for (const relativePath of keyFiles) {
92
+ if (await pathExists(path.join(repoRoot, relativePath))) {
93
+ resolvedKeyFiles.push(relativePath);
94
+ }
95
+ }
96
+
97
+ const resolvedDocCandidates = [];
98
+ for (const relativePath of docCandidates) {
99
+ if (await pathExists(path.join(repoRoot, relativePath))) {
100
+ resolvedDocCandidates.push(relativePath);
101
+ }
102
+ }
103
+
104
+ const languages = inferLanguages(fileNames.concat(resolvedKeyFiles));
105
+ const frameworkHints = inferFrameworkHints(fileNames, dirNames);
106
+ const packageManager = inferPackageManager(fileNames);
107
+ const workspaceLayout = buildWorkspaceLayout(dirNames);
108
+ const commandHints = [];
109
+ if (packageManager) {
110
+ commandHints.push(`${packageManager} test`);
111
+ commandHints.push(`${packageManager} run gen-admin`);
112
+ }
113
+
114
+ return {
115
+ id: `project_probe.${repoName}.${(workspaceId || 'default').replace(/[^a-zA-Z0-9_.-]/g, '_')}`,
116
+ clientProfileId,
117
+ connectorId,
118
+ connectorType,
119
+ projectId: repoName.toLowerCase(),
120
+ workspaceId: workspaceId || null,
121
+ repoName,
122
+ repoRoot,
123
+ gitRemote: null,
124
+ defaultBranch: 'main',
125
+ languages,
126
+ frameworkHints,
127
+ packageManager: packageManager || null,
128
+ workspaceLayout: workspaceLayout || null,
129
+ keyFiles: resolvedKeyFiles,
130
+ docCandidates: resolvedDocCandidates,
131
+ commandHints,
132
+ summary: `${repoName} 项目骨架自动探测结果`,
133
+ notes: '由本地 connector bridge 自动生成,后续应由 CloudMCP 生成候选上下文包并进入确认流程。',
134
+ generatedBy,
135
+ status: 'ready_for_context'
136
+ };
137
+ }
@@ -0,0 +1,315 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+
5
+ const DEFAULT_SERVER_NAME = 'cloudmcp';
6
+ const DEFAULT_PACKAGE_NAME = 'cloudflare-mcp-smart-proxy';
7
+ const DEFAULT_PACKAGE_BIN = 'cloudflare-mcp-proxy';
8
+ const MANAGED_BLOCK_PREFIX = '# BEGIN CLOUDMCP MANAGED MCP SERVER';
9
+ const MANAGED_BLOCK_SUFFIX = '# END CLOUDMCP MANAGED MCP SERVER';
10
+
11
+ function normalizeString(value, fallback = '') {
12
+ const normalized = typeof value === 'string' ? value.trim() : '';
13
+ return normalized || fallback;
14
+ }
15
+
16
+ function normalizeEnvValue(value) {
17
+ if (value == null) return '';
18
+ return String(value);
19
+ }
20
+
21
+ function sanitizeServerName(value) {
22
+ const candidate = normalizeString(value, DEFAULT_SERVER_NAME)
23
+ .replace(/[^a-zA-Z0-9_-]/g, '_');
24
+ return candidate || DEFAULT_SERVER_NAME;
25
+ }
26
+
27
+ function resolveHomeDir(homeDir = null) {
28
+ return homeDir || os.homedir();
29
+ }
30
+
31
+ function buildWorkspaceId(workspaceRoot) {
32
+ return `workspace.${path.basename(workspaceRoot || process.cwd()).replace(/[^a-zA-Z0-9_.-]/g, '_')}`;
33
+ }
34
+
35
+ function buildConnectorId(ecosystem, workspaceRoot) {
36
+ return `connector.${sanitizeServerName(ecosystem)}.${path.basename(workspaceRoot || process.cwd()).replace(/[^a-zA-Z0-9_.-]/g, '_')}`;
37
+ }
38
+
39
+ function ensureDir(targetPath) {
40
+ const dirPath = path.dirname(targetPath);
41
+ if (!fs.existsSync(dirPath)) {
42
+ fs.mkdirSync(dirPath, { recursive: true });
43
+ }
44
+ }
45
+
46
+ function readJson(filePath, fallback) {
47
+ try {
48
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
49
+ } catch {
50
+ return fallback;
51
+ }
52
+ }
53
+
54
+ function escapeTomlString(value) {
55
+ return String(value)
56
+ .replace(/\\/g, '\\\\')
57
+ .replace(/"/g, '\\"');
58
+ }
59
+
60
+ function formatTomlArray(values) {
61
+ return `[${values.map((value) => `"${escapeTomlString(value)}"`).join(', ')}]`;
62
+ }
63
+
64
+ function toManagedEnv({
65
+ cloudUrl,
66
+ cloudApiKey,
67
+ workspaceRoot,
68
+ clientProfileId,
69
+ connectorId,
70
+ connectorType,
71
+ workspaceId
72
+ }) {
73
+ const env = {
74
+ CLOUDFLARE_MCP_URL: normalizeEnvValue(cloudUrl),
75
+ CLOUDFLARE_MCP_API_KEY: normalizeEnvValue(cloudApiKey),
76
+ WORKSPACE_ROOT: normalizeEnvValue(workspaceRoot),
77
+ CLOUDMCP_CLIENT_PROFILE_ID: normalizeEnvValue(clientProfileId),
78
+ CLOUDMCP_CONNECTOR_ID: normalizeEnvValue(connectorId),
79
+ CLOUDMCP_CONNECTOR_TYPE: normalizeEnvValue(connectorType),
80
+ CLOUDMCP_WORKSPACE_ID: normalizeEnvValue(workspaceId)
81
+ };
82
+
83
+ return Object.fromEntries(
84
+ Object.entries(env).filter(([, value]) => value !== '')
85
+ );
86
+ }
87
+
88
+ export function resolveReferenceConnectorCommand({
89
+ packageRoot = null,
90
+ runtime = 'npm'
91
+ } = {}) {
92
+ if (runtime === 'npm') {
93
+ if (process.platform === 'win32') {
94
+ return {
95
+ command: 'cmd',
96
+ args: ['/c', 'npx', '-y', '-p', DEFAULT_PACKAGE_NAME, DEFAULT_PACKAGE_BIN]
97
+ };
98
+ }
99
+ return {
100
+ command: 'npx',
101
+ args: ['-y', '-p', DEFAULT_PACKAGE_NAME, DEFAULT_PACKAGE_BIN]
102
+ };
103
+ }
104
+
105
+ const resolvedPackageRoot = path.resolve(packageRoot || path.join(process.cwd(), 'local-proxy'));
106
+ return {
107
+ command: process.execPath,
108
+ args: [path.join(resolvedPackageRoot, 'index.js')]
109
+ };
110
+ }
111
+
112
+ export function buildReferenceConnectorServer({
113
+ ecosystem,
114
+ cloudUrl,
115
+ cloudApiKey,
116
+ workspaceRoot = process.cwd(),
117
+ clientProfileId,
118
+ connectorId = '',
119
+ connectorType = '',
120
+ workspaceId = '',
121
+ packageRoot = null,
122
+ runtime = 'npm'
123
+ }) {
124
+ const normalizedEcosystem = normalizeString(ecosystem).toLowerCase();
125
+ const resolvedWorkspaceRoot = path.resolve(workspaceRoot || process.cwd());
126
+ const resolvedConnectorType = normalizeString(connectorType, normalizedEcosystem || 'reference_connector');
127
+ const resolvedWorkspaceId = normalizeString(workspaceId, buildWorkspaceId(resolvedWorkspaceRoot));
128
+ const resolvedConnectorId = normalizeString(connectorId, buildConnectorId(normalizedEcosystem, resolvedWorkspaceRoot));
129
+ const commandDescriptor = resolveReferenceConnectorCommand({ packageRoot, runtime });
130
+
131
+ return {
132
+ type: 'stdio',
133
+ command: commandDescriptor.command,
134
+ args: commandDescriptor.args,
135
+ env: toManagedEnv({
136
+ cloudUrl,
137
+ cloudApiKey,
138
+ workspaceRoot: resolvedWorkspaceRoot,
139
+ clientProfileId,
140
+ connectorId: resolvedConnectorId,
141
+ connectorType: resolvedConnectorType,
142
+ workspaceId: resolvedWorkspaceId
143
+ }),
144
+ enabled: true,
145
+ connectorId: resolvedConnectorId,
146
+ connectorType: resolvedConnectorType,
147
+ workspaceId: resolvedWorkspaceId
148
+ };
149
+ }
150
+
151
+ export function getReferenceConnectorTarget({
152
+ ecosystem,
153
+ workspaceRoot = process.cwd(),
154
+ scope = 'user',
155
+ homeDir = null
156
+ }) {
157
+ const normalizedEcosystem = normalizeString(ecosystem).toLowerCase();
158
+ const normalizedScope = normalizeString(scope, 'user').toLowerCase();
159
+ const resolvedWorkspaceRoot = path.resolve(workspaceRoot || process.cwd());
160
+ const resolvedHomeDir = resolveHomeDir(homeDir);
161
+
162
+ if (normalizedEcosystem === 'codex') {
163
+ return {
164
+ ecosystem: normalizedEcosystem,
165
+ scope: 'user',
166
+ format: 'toml',
167
+ targetFile: path.join(resolvedHomeDir, '.codex', 'config.toml')
168
+ };
169
+ }
170
+
171
+ if (normalizedEcosystem === 'claude_code') {
172
+ if (normalizedScope === 'project') {
173
+ return {
174
+ ecosystem: normalizedEcosystem,
175
+ scope: 'project',
176
+ format: 'json',
177
+ targetFile: path.join(resolvedWorkspaceRoot, '.mcp.json')
178
+ };
179
+ }
180
+ return {
181
+ ecosystem: normalizedEcosystem,
182
+ scope: 'user',
183
+ format: 'json',
184
+ targetFile: path.join(resolvedHomeDir, '.claude.json')
185
+ };
186
+ }
187
+
188
+ throw new Error(`Unsupported reference connector ecosystem "${ecosystem}"`);
189
+ }
190
+
191
+ export function buildCodexManagedBlock(serverName, serverDefinition) {
192
+ const lines = [
193
+ `${MANAGED_BLOCK_PREFIX} ${serverName}`,
194
+ `[mcp_servers.${serverName}]`,
195
+ `command = "${escapeTomlString(serverDefinition.command)}"`,
196
+ `args = ${formatTomlArray(serverDefinition.args || [])}`,
197
+ `enabled = ${serverDefinition.enabled === false ? 'false' : 'true'}`
198
+ ];
199
+
200
+ const envEntries = Object.entries(serverDefinition.env || {});
201
+ if (envEntries.length > 0) {
202
+ lines.push('', `[mcp_servers.${serverName}.env]`);
203
+ for (const [key, value] of envEntries) {
204
+ lines.push(`${key} = "${escapeTomlString(value)}"`);
205
+ }
206
+ }
207
+
208
+ lines.push(MANAGED_BLOCK_SUFFIX);
209
+ return `${lines.join('\n')}\n`;
210
+ }
211
+
212
+ function replaceManagedBlock(existingContent, serverName, nextBlock) {
213
+ const startMarker = `${MANAGED_BLOCK_PREFIX} ${serverName}`;
214
+ const endMarker = MANAGED_BLOCK_SUFFIX;
215
+ const pattern = new RegExp(
216
+ `${startMarker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${endMarker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\n?`,
217
+ 'g'
218
+ );
219
+ const withoutPrevious = String(existingContent || '').replace(pattern, '').trimEnd();
220
+ if (!withoutPrevious) {
221
+ return nextBlock;
222
+ }
223
+ return `${withoutPrevious}\n\n${nextBlock}`;
224
+ }
225
+
226
+ export function mergeCodexConfig(existingContent, serverName, serverDefinition) {
227
+ return replaceManagedBlock(existingContent, serverName, buildCodexManagedBlock(serverName, serverDefinition));
228
+ }
229
+
230
+ export function mergeClaudeCodeConfig(existingConfig, serverName, serverDefinition) {
231
+ const nextConfig = existingConfig && typeof existingConfig === 'object' ? { ...existingConfig } : {};
232
+ nextConfig.mcpServers = nextConfig.mcpServers && typeof nextConfig.mcpServers === 'object'
233
+ ? { ...nextConfig.mcpServers }
234
+ : {};
235
+ nextConfig.mcpServers[serverName] = {
236
+ command: serverDefinition.command,
237
+ args: [...(serverDefinition.args || [])],
238
+ env: { ...(serverDefinition.env || {}) }
239
+ };
240
+ return nextConfig;
241
+ }
242
+
243
+ export function installReferenceConnector({
244
+ ecosystem,
245
+ cloudUrl,
246
+ cloudApiKey,
247
+ workspaceRoot = process.cwd(),
248
+ clientProfileId,
249
+ connectorId = '',
250
+ connectorType = '',
251
+ workspaceId = '',
252
+ packageRoot = null,
253
+ runtime = 'npm',
254
+ scope = 'user',
255
+ serverName = DEFAULT_SERVER_NAME,
256
+ homeDir = null,
257
+ dryRun = false
258
+ }) {
259
+ const normalizedServerName = sanitizeServerName(serverName);
260
+ const target = getReferenceConnectorTarget({
261
+ ecosystem,
262
+ workspaceRoot,
263
+ scope,
264
+ homeDir
265
+ });
266
+ const serverDefinition = buildReferenceConnectorServer({
267
+ ecosystem,
268
+ cloudUrl,
269
+ cloudApiKey,
270
+ workspaceRoot,
271
+ clientProfileId,
272
+ connectorId,
273
+ connectorType,
274
+ workspaceId,
275
+ packageRoot,
276
+ runtime
277
+ });
278
+
279
+ let renderedContent = '';
280
+ if (target.format === 'toml') {
281
+ const existingContent = fs.existsSync(target.targetFile)
282
+ ? fs.readFileSync(target.targetFile, 'utf8')
283
+ : '';
284
+ renderedContent = mergeCodexConfig(existingContent, normalizedServerName, serverDefinition);
285
+ } else {
286
+ const existingConfig = readJson(target.targetFile, {});
287
+ renderedContent = JSON.stringify(
288
+ mergeClaudeCodeConfig(existingConfig, normalizedServerName, serverDefinition),
289
+ null,
290
+ 2
291
+ );
292
+ }
293
+
294
+ if (!dryRun) {
295
+ ensureDir(target.targetFile);
296
+ fs.writeFileSync(target.targetFile, renderedContent, 'utf8');
297
+ }
298
+
299
+ return {
300
+ ecosystem: target.ecosystem,
301
+ scope: target.scope,
302
+ targetFile: target.targetFile,
303
+ serverName: normalizedServerName,
304
+ serverDefinition,
305
+ renderedContent,
306
+ written: !dryRun
307
+ };
308
+ }
309
+
310
+ export function printReferenceConnectorConfig(options) {
311
+ return installReferenceConnector({
312
+ ...options,
313
+ dryRun: true
314
+ });
315
+ }
package/src/router.js CHANGED
@@ -3,11 +3,12 @@
3
3
  */
4
4
 
5
5
  export class SmartRouter {
6
- constructor(cloudUrl, cloudApiKey, localTools, workspaceRoot = null) {
6
+ constructor(cloudUrl, cloudApiKey, localTools, workspaceRoot = null, deviceIdentity = null) {
7
7
  this.cloudUrl = cloudUrl.replace(/\/$/, ''); // 移除尾部斜杠
8
8
  this.cloudApiKey = cloudApiKey;
9
9
  this.localTools = localTools;
10
10
  this.workspaceRoot = workspaceRoot || process.cwd();
11
+ this.deviceIdentity = deviceIdentity;
11
12
 
12
13
  // 工具路由规则
13
14
  this.routingRules = {
@@ -27,7 +28,12 @@ export class SmartRouter {
27
28
  'get_document_symbols',
28
29
  'replace_lines',
29
30
  'execute_command',
30
- 'execute_shell_command'
31
+ 'execute_shell_command',
32
+ 'connector_bridge_status',
33
+ 'connector_sync_profile',
34
+ 'connector_report_status',
35
+ 'connector_discover_project_probe',
36
+ 'connector_report_project_probe'
31
37
  ],
32
38
 
33
39
  // 云端工具(需要网络或云端资源)
@@ -151,21 +157,26 @@ export class SmartRouter {
151
157
  }
152
158
 
153
159
  try {
160
+ const body = JSON.stringify({
161
+ jsonrpc: '2.0',
162
+ id: Date.now(),
163
+ method: 'tools/call',
164
+ params: {
165
+ name: toolName,
166
+ arguments: params
167
+ }
168
+ });
169
+ const signedHeaders = this.deviceIdentity
170
+ ? await this.deviceIdentity.buildSignedHeaders({ method: 'POST', pathname: '/mcp', body })
171
+ : {};
154
172
  const response = await fetch(`${this.cloudUrl}/mcp`, {
155
173
  method: 'POST',
156
174
  headers: {
157
175
  'Authorization': `Bearer ${this.cloudApiKey}`,
158
- 'Content-Type': 'application/json'
176
+ 'Content-Type': 'application/json',
177
+ ...signedHeaders
159
178
  },
160
- body: JSON.stringify({
161
- jsonrpc: '2.0',
162
- id: Date.now(),
163
- method: 'tools/call',
164
- params: {
165
- name: toolName,
166
- arguments: params
167
- }
168
- })
179
+ body
169
180
  });
170
181
 
171
182
  if (!response.ok) {
@@ -263,18 +274,23 @@ export class SmartRouter {
263
274
  */
264
275
  async getCloudTools() {
265
276
  try {
277
+ const body = JSON.stringify({
278
+ jsonrpc: '2.0',
279
+ id: Date.now(),
280
+ method: 'tools/list',
281
+ params: {}
282
+ });
283
+ const signedHeaders = this.deviceIdentity
284
+ ? await this.deviceIdentity.buildSignedHeaders({ method: 'POST', pathname: '/mcp', body })
285
+ : {};
266
286
  const response = await fetch(`${this.cloudUrl}/mcp`, {
267
287
  method: 'POST',
268
288
  headers: {
269
289
  'Authorization': `Bearer ${this.cloudApiKey}`,
270
- 'Content-Type': 'application/json'
290
+ 'Content-Type': 'application/json',
291
+ ...signedHeaders
271
292
  },
272
- body: JSON.stringify({
273
- jsonrpc: '2.0',
274
- id: Date.now(),
275
- method: 'tools/list',
276
- params: {}
277
- })
293
+ body
278
294
  });
279
295
 
280
296
  if (!response.ok) {
@@ -294,4 +310,3 @@ export class SmartRouter {
294
310
  }
295
311
  }
296
312
  }
297
-
@@ -1,28 +0,0 @@
1
- {
2
- "name": "cloudflare-mcp-smart-proxy",
3
- "version": "1.1.1",
4
- "description": "Smart proxy for Cloudflare MCP - routes tools to cloud or local execution",
5
- "type": "module",
6
- "main": "index.js",
7
- "bin": {
8
- "cloudflare-mcp-proxy": "./index.js"
9
- },
10
- "scripts": {
11
- "start": "node index.js"
12
- },
13
- "keywords": [
14
- "mcp",
15
- "cloudflare",
16
- "proxy",
17
- "local",
18
- "filesystem"
19
- ],
20
- "author": "",
21
- "license": "MIT",
22
- "dependencies": {
23
- "@modelcontextprotocol/sdk": "^1.0.0"
24
- },
25
- "engines": {
26
- "node": ">=18.0.0"
27
- }
28
- }