@szc-ft/mcp-szcd-client 0.17.0 → 0.18.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@szc-ft/mcp-szcd-client",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "MCP client for szcd component library - auto-configures AI coding tools with MCP server, skills, agents and commands",
5
5
  "keywords": [
6
6
  "mcp",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "szcd-component-helper",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "szcd 组件库 MCP 助手 — 查询组件信息、匹配需求、生成代码",
5
5
  "mcpServers": {
6
6
  "szcd-component-helper": {
@@ -19,7 +19,7 @@ compatibility:
19
19
 
20
20
  **重要说明**:这是一个客户端 skill,通过 MCP 协议(SSE 或 HTTP)连接到远程 MCP 服务器。服务器由管理员维护,包含源码和数据。
21
21
 
22
- **版本**:v0.17.0 - 新增 10 段网段本地网络兼容:api_tool 新增 parse_swagger_json action,客户端新增 local-api-tool 技能,遇到 10.x.x.x 网段自动在本地 curl 获取 Swagger JSON 再交由服务端解析
22
+ **版本**:v0.18.0 - 新增 MCP 鉴权支持:postinstall 自动同步 API Key 到 IDE 配置,szcd-mcp-auth 命令支持 set/clear 并自动更新所有 IDE mcp.json
23
23
 
24
24
  ## 架构说明
25
25
 
@@ -9,7 +9,7 @@ import fs from "node:fs";
9
9
  import path from "node:path";
10
10
  import os from "node:os";
11
11
  import { execSync } from "node:child_process";
12
- import { getClientConfigHeader as getClientConfigHeaderDirect } from "./common.js";
12
+ import { getClientConfigHeader as getClientConfigHeaderDirect, getApiKey as getApiKeyDirect } from "./common.js";
13
13
 
14
14
  // ==================== 路径工具 ====================
15
15
 
@@ -55,16 +55,21 @@ function syncClaudeCodeConfig(deps) {
55
55
  if (!config.mcpServers) config.mcpServers = {};
56
56
 
57
57
  const current = config.mcpServers[serverName];
58
- if (current && current.type === "http" && current.url === mcpUrl) {
58
+ const apiKey = deps.getApiKey ? deps.getApiKey() : "";
59
+ const needsApiKeyUpdate = apiKey && (!current || !current.headers || current.headers["X-API-Key"] !== apiKey);
60
+ if (current && current.type === "http" && current.url === mcpUrl && !needsApiKeyUpdate) {
59
61
  console.log(`✓ Claude Code ~/.claude.json already up-to-date: ${mcpUrl}`);
60
62
  return;
61
63
  }
62
64
 
63
65
  const clientConfigHeader = deps.getClientConfigHeader ? deps.getClientConfigHeader() : null;
66
+ const headers = {};
67
+ if (clientConfigHeader) headers["X-Client-Config"] = clientConfigHeader;
68
+ if (apiKey) headers["X-API-Key"] = apiKey;
64
69
  config.mcpServers[serverName] = {
65
70
  type: "http",
66
71
  url: mcpUrl,
67
- ...(clientConfigHeader ? { headers: { "X-Client-Config": clientConfigHeader } } : {}),
72
+ ...(Object.keys(headers).length > 0 ? { headers } : {}),
68
73
  };
69
74
 
70
75
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
@@ -316,16 +321,21 @@ function syncClaudeCodeConfigDirect(targetUrl, serverName) {
316
321
  if (!config.mcpServers) config.mcpServers = {};
317
322
 
318
323
  const current = config.mcpServers[serverName];
319
- if (current && current.type === "http" && current.url === mcpUrl) {
324
+ const apiKey = getApiKeyDirect();
325
+ const needsApiKeyUpdate = apiKey && (!current || !current.headers || current.headers["X-API-Key"] !== apiKey);
326
+ if (current && current.type === "http" && current.url === mcpUrl && !needsApiKeyUpdate) {
320
327
  console.log(`⏭️ Claude Code ~/.claude.json already up-to-date: ${mcpUrl}`);
321
328
  return;
322
329
  }
323
330
 
324
331
  const clientConfigHeader = getClientConfigHeaderDirect();
332
+ const headers = {};
333
+ if (clientConfigHeader) headers["X-Client-Config"] = clientConfigHeader;
334
+ if (apiKey) headers["X-API-Key"] = apiKey;
325
335
  config.mcpServers[serverName] = {
326
336
  type: "http",
327
337
  url: mcpUrl,
328
- ...(clientConfigHeader ? { headers: { "X-Client-Config": clientConfigHeader } } : {}),
338
+ ...(Object.keys(headers).length > 0 ? { headers } : {}),
329
339
  };
330
340
 
331
341
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
@@ -209,6 +209,15 @@ export function encodeClientConfig(config) {
209
209
  return Buffer.from(JSON.stringify(config)).toString("base64");
210
210
  }
211
211
 
212
+ /**
213
+ * 获取 MCP_API_KEY(从用户本地配置文件读取)
214
+ * 用于通过 X-API-Key Header 传递给 MCP 服务器鉴权
215
+ */
216
+ export function getApiKey() {
217
+ const config = loadExistingConfig();
218
+ return config.MCP_API_KEY || "";
219
+ }
220
+
212
221
  /**
213
222
  * 获取 X-Client-Config Header 值(便捷函数)
214
223
  * 返回 Base64 编码的字符串,或 null(无 CODING 配置时)
@@ -216,3 +225,20 @@ export function encodeClientConfig(config) {
216
225
  export function getClientConfigHeader() {
217
226
  return encodeClientConfig(loadClientCodingConfig());
218
227
  }
228
+
229
+ /**
230
+ * 构建 MCP HTTP 请求的 headers 对象(包含 X-Client-Config 和 X-API-Key)
231
+ * @returns {Object} headers 对象,可能为空对象
232
+ */
233
+ export function buildMcpHeaders() {
234
+ const headers = {};
235
+ const clientConfigHeader = getClientConfigHeader();
236
+ if (clientConfigHeader) {
237
+ headers["X-Client-Config"] = clientConfigHeader;
238
+ }
239
+ const apiKey = getApiKey();
240
+ if (apiKey) {
241
+ headers["X-API-Key"] = apiKey;
242
+ }
243
+ return Object.keys(headers).length > 0 ? headers : null;
244
+ }
@@ -29,7 +29,7 @@ import fs from "node:fs";
29
29
  import path from "node:path";
30
30
  import os from "node:os";
31
31
  import { execSync } from "node:child_process";
32
- import { getClientConfigHeader as _getClientConfigHeader } from "./common.js";
32
+ import { getClientConfigHeader as _getClientConfigHeader, getApiKey as _getApiKey } from "./common.js";
33
33
 
34
34
  // ==================== 路径工具 ====================
35
35
 
@@ -150,14 +150,19 @@ function syncQoderSettings(deps) {
150
150
  if (!settings.mcpServers) settings.mcpServers = {};
151
151
 
152
152
  const clientConfigHeader = deps.getClientConfigHeader ? deps.getClientConfigHeader() : null;
153
+ const apiKey = deps.getApiKey ? deps.getApiKey() : "";
154
+ const headers = {};
155
+ if (clientConfigHeader) headers["X-Client-Config"] = clientConfigHeader;
156
+ if (apiKey) headers["X-API-Key"] = apiKey;
153
157
  const desiredConfig = {
154
158
  type: "http",
155
159
  url: mcpUrl,
156
- ...(clientConfigHeader ? { headers: { "X-Client-Config": clientConfigHeader } } : {}),
160
+ ...(Object.keys(headers).length > 0 ? { headers } : {}),
157
161
  };
158
162
 
159
163
  const current = settings.mcpServers[serverName];
160
- if (current && current.type === "http" && current.url === mcpUrl) {
164
+ const needsApiKeyUpdate = apiKey && (!current || !current.headers || current.headers["X-API-Key"] !== apiKey);
165
+ if (current && current.type === "http" && current.url === mcpUrl && !needsApiKeyUpdate) {
161
166
  console.log(`✓ Qoder ~/.qoder/settings.json already up-to-date: ${serverName} (${mcpUrl})`);
162
167
  return;
163
168
  }
@@ -366,10 +371,14 @@ function setupQoderProjectConfig(deps) {
366
371
  if (!settings.mcpServers) settings.mcpServers = {};
367
372
 
368
373
  const clientConfigHeader = deps.getClientConfigHeader ? deps.getClientConfigHeader() : null;
374
+ const _apiKey = deps.getApiKey ? deps.getApiKey() : "";
375
+ const _headers = {};
376
+ if (clientConfigHeader) _headers["X-Client-Config"] = clientConfigHeader;
377
+ if (_apiKey) _headers["X-API-Key"] = _apiKey;
369
378
  settings.mcpServers[serverName] = {
370
379
  type: "http",
371
380
  url: mcpUrl,
372
- ...(clientConfigHeader ? { headers: { "X-Client-Config": clientConfigHeader } } : {}),
381
+ ...(Object.keys(_headers).length > 0 ? { headers: _headers } : {}),
373
382
  };
374
383
 
375
384
  writeJsonFile(projectSettingsPath, settings);
@@ -444,16 +453,21 @@ function syncQoderSettingsDirect(targetUrl, serverName) {
444
453
  if (!settings.mcpServers) settings.mcpServers = {};
445
454
 
446
455
  const current = settings.mcpServers[serverName];
447
- if (current && current.type === "http" && current.url === mcpUrl) {
456
+ const _apiKey = _getApiKey();
457
+ const needsApiKeyUpdate = _apiKey && (!current || !current.headers || current.headers["X-API-Key"] !== _apiKey);
458
+ if (current && current.type === "http" && current.url === mcpUrl && !needsApiKeyUpdate) {
448
459
  console.log(`⏭️ Qoder ~/.qoder/settings.json already up-to-date: ${serverName} (${mcpUrl})`);
449
460
  return;
450
461
  }
451
462
 
452
463
  const clientConfigHeader = _getClientConfigHeader();
464
+ const _headers = {};
465
+ if (clientConfigHeader) _headers["X-Client-Config"] = clientConfigHeader;
466
+ if (_apiKey) _headers["X-API-Key"] = _apiKey;
453
467
  settings.mcpServers[serverName] = {
454
468
  type: "http",
455
469
  url: mcpUrl,
456
- ...(clientConfigHeader ? { headers: { "X-Client-Config": clientConfigHeader } } : {}),
470
+ ...(Object.keys(_headers).length > 0 ? { headers: _headers } : {}),
457
471
  };
458
472
 
459
473
  writeJsonFile(settingsPath, settings);
@@ -23,7 +23,7 @@ import fs from "node:fs";
23
23
  import path from "node:path";
24
24
  import os from "node:os";
25
25
  import { execSync } from "node:child_process";
26
- import { getClientConfigHeader as getClientConfigHeaderDirect } from "./common.js";
26
+ import { getClientConfigHeader as getClientConfigHeaderDirect, getApiKey as getApiKeyDirect } from "./common.js";
27
27
 
28
28
  // ==================== 路径工具 ====================
29
29
 
@@ -314,13 +314,17 @@ function injectClientConfigHeader(serverName, clientConfigHeader) {
314
314
  if (!manifest.mcpServers || !manifest.mcpServers[serverName]) return;
315
315
 
316
316
  const entry = manifest.mcpServers[serverName];
317
- if (entry.headers && entry.headers["X-Client-Config"] === clientConfigHeader) {
317
+ const _apiKey = getApiKeyDirect();
318
+ if (entry.headers && entry.headers["X-Client-Config"] === clientConfigHeader && entry.headers["X-API-Key"] === _apiKey) {
318
319
  return; // 已是最新
319
320
  }
320
321
 
321
- entry.headers = { "X-Client-Config": clientConfigHeader };
322
+ const _headers = {};
323
+ if (clientConfigHeader) _headers["X-Client-Config"] = clientConfigHeader;
324
+ if (_apiKey) _headers["X-API-Key"] = _apiKey;
325
+ entry.headers = _headers;
322
326
  writeJsonFile(manifestPath, manifest);
323
- console.log(`✓ Injected X-Client-Config header into extension qwen-extension.json`);
327
+ console.log(`✓ Injected X-Client-Config and X-API-Key headers into extension qwen-extension.json`);
324
328
  }
325
329
 
326
330
  /**
@@ -343,10 +347,11 @@ function syncExtensionConfig(targetUrl, serverName) {
343
347
 
344
348
  const mcpUrl = `${targetUrl}/mcp`;
345
349
  const clientConfigHeader = getClientConfigHeaderDirect();
350
+ const _apiKey = getApiKeyDirect();
346
351
  const entry = manifest.mcpServers[serverName];
347
352
  const headersUnchanged = clientConfigHeader
348
- ? entry.headers && entry.headers["X-Client-Config"] === clientConfigHeader
349
- : !entry.headers;
353
+ ? entry.headers && entry.headers["X-Client-Config"] === clientConfigHeader && entry.headers["X-API-Key"] === _apiKey
354
+ : !entry.headers || (!_apiKey && !entry.headers["X-API-Key"]);
350
355
  if (entry.type === "http" && entry.httpUrl === mcpUrl && headersUnchanged) {
351
356
  console.log(`⏭️ Extension qwen-extension.json already up-to-date: ${mcpUrl}`);
352
357
  return;
@@ -356,9 +361,12 @@ function syncExtensionConfig(targetUrl, serverName) {
356
361
  entry.httpUrl = mcpUrl;
357
362
  // 清理旧字段,避免冲突
358
363
  delete entry.url;
359
- // 透传用户本地 CODING 配置
360
- if (clientConfigHeader) {
361
- entry.headers = { "X-Client-Config": clientConfigHeader };
364
+ // 透传用户本地 CODING 配置和 API Key
365
+ const _headers = {};
366
+ if (clientConfigHeader) _headers["X-Client-Config"] = clientConfigHeader;
367
+ if (_apiKey) _headers["X-API-Key"] = _apiKey;
368
+ if (Object.keys(_headers).length > 0) {
369
+ entry.headers = _headers;
362
370
  } else {
363
371
  delete entry.headers;
364
372
  }
@@ -396,10 +404,11 @@ export function setupQwenCode(deps) {
396
404
  extensionInstalled = installExtensionViaCopy(deps);
397
405
  }
398
406
 
399
- // ---- 注入 X-Client-Config Header ----
407
+ // ---- 注入 X-Client-Config / X-API-Key Header ----
400
408
  if (extensionInstalled) {
401
409
  const clientConfigHeader = deps.getClientConfigHeader ? deps.getClientConfigHeader() : null;
402
- if (clientConfigHeader) {
410
+ const apiKey = deps.getApiKey ? deps.getApiKey() : "";
411
+ if (clientConfigHeader || apiKey) {
403
412
  injectClientConfigHeader(deps.getMcpServerName(), clientConfigHeader);
404
413
  }
405
414
  }
@@ -14,7 +14,7 @@ import fs from "node:fs";
14
14
  import path from "node:path";
15
15
  import os from "node:os";
16
16
  import { execSync } from "node:child_process";
17
- import { loadClientCodingConfig, encodeClientConfig } from "./common.js";
17
+ import { loadClientCodingConfig, encodeClientConfig, getApiKey } from "./common.js";
18
18
 
19
19
  // ==================== 路径工具 ====================
20
20
 
@@ -89,8 +89,9 @@ export function syncTraeCliYaml(deps) {
89
89
 
90
90
  // 读取用户本地 CODING 配置,生成 X-Client-Config Header
91
91
  const clientConfigHeader = deps.getClientConfigHeader ? deps.getClientConfigHeader() : null;
92
- const headersBlock = clientConfigHeader
93
- ? `\n headers:\n X-Client-Config: ${clientConfigHeader}`
92
+ const apiKey = deps.getApiKey ? deps.getApiKey() : "";
93
+ const headersBlock = clientConfigHeader || apiKey
94
+ ? `\n headers:${clientConfigHeader ? `\n X-Client-Config: ${clientConfigHeader}` : ""}${apiKey ? `\n X-API-Key: ${apiKey}` : ""}`
94
95
  : "";
95
96
 
96
97
  if (!fs.existsSync(yamlPath)) {
@@ -147,10 +148,12 @@ export function createTraeCliConfig(deps) {
147
148
  // 读取用户本地 CODING 配置,生成 X-Client-Config Header
148
149
  const codingConfig = loadClientCodingConfig();
149
150
  const clientConfigHeader = encodeClientConfig(codingConfig);
151
+ const apiKey = getApiKey();
150
152
  const jsonConfigObj = { type: "http", url: mcpUrl };
151
- if (clientConfigHeader) {
152
- jsonConfigObj.headers = { "X-Client-Config": clientConfigHeader };
153
- }
153
+ const _headers = {};
154
+ if (clientConfigHeader) _headers["X-Client-Config"] = clientConfigHeader;
155
+ if (apiKey) _headers["X-API-Key"] = apiKey;
156
+ if (Object.keys(_headers).length > 0) jsonConfigObj.headers = _headers;
154
157
  const jsonConfig = JSON.stringify(jsonConfigObj);
155
158
 
156
159
  deps.safeExecSync(`trae-cli mcp remove ${serverName} 2>/dev/null`);
@@ -13,7 +13,7 @@
13
13
  import fs from "node:fs";
14
14
  import path from "node:path";
15
15
  import os from "node:os";
16
- import { getClientConfigHeader } from "./common.js";
16
+ import { getClientConfigHeader, getApiKey } from "./common.js";
17
17
 
18
18
  // ==================== 路径工具 ====================
19
19
 
@@ -74,7 +74,9 @@ export function syncTraeCnMcpJson(deps) {
74
74
  if (!config.mcpServers) config.mcpServers = {};
75
75
 
76
76
  const current = config.mcpServers[serverName];
77
- if (current && current.url === mcpUrl && current.type === "http") {
77
+ const apiKey = deps.getApiKey ? deps.getApiKey() : "";
78
+ const needsApiKeyUpdate = apiKey && (!current || !current.headers || current.headers["X-API-Key"] !== apiKey);
79
+ if (current && current.url === mcpUrl && current.type === "http" && !needsApiKeyUpdate) {
78
80
  console.log(`✓ Trae CN mcp.json already up-to-date: ${mcpUrl}`);
79
81
  return;
80
82
  }
@@ -84,9 +86,10 @@ export function syncTraeCnMcpJson(deps) {
84
86
  type: "http",
85
87
  url: mcpUrl,
86
88
  };
87
- if (clientConfigHeader) {
88
- serverConfig.headers = { "X-Client-Config": clientConfigHeader };
89
- }
89
+ const headers = {};
90
+ if (clientConfigHeader) headers["X-Client-Config"] = clientConfigHeader;
91
+ if (apiKey) headers["X-API-Key"] = apiKey;
92
+ if (Object.keys(headers).length > 0) serverConfig.headers = headers;
90
93
 
91
94
  config.mcpServers[serverName] = serverConfig;
92
95
 
@@ -121,7 +124,9 @@ export function syncMcpUrl(targetUrl, serverName) {
121
124
  if (!config.mcpServers) config.mcpServers = {};
122
125
 
123
126
  const current = config.mcpServers[serverName];
124
- if (current && current.url === mcpUrl && current.type === "http") {
127
+ const apiKey = getApiKey();
128
+ const needsApiKeyUpdate = apiKey && (!current || !current.headers || current.headers["X-API-Key"] !== apiKey);
129
+ if (current && current.url === mcpUrl && current.type === "http" && !needsApiKeyUpdate) {
125
130
  console.log(`⏭️ Trae CN mcp.json already up-to-date: ${mcpUrl}`);
126
131
  return;
127
132
  }
@@ -131,9 +136,10 @@ export function syncMcpUrl(targetUrl, serverName) {
131
136
  type: "http",
132
137
  url: mcpUrl,
133
138
  };
134
- if (clientConfigHeader) {
135
- serverConfig.headers = { "X-Client-Config": clientConfigHeader };
136
- }
139
+ const headers = {};
140
+ if (clientConfigHeader) headers["X-Client-Config"] = clientConfigHeader;
141
+ if (apiKey) headers["X-API-Key"] = apiKey;
142
+ if (Object.keys(headers).length > 0) serverConfig.headers = headers;
137
143
 
138
144
  config.mcpServers[serverName] = serverConfig;
139
145
 
@@ -34,6 +34,7 @@ const deps = {
34
34
  getMcpServerUrl: common.getMcpServerUrl,
35
35
  getMcpServerName: common.getMcpServerName,
36
36
  getClientConfigHeader: common.getClientConfigHeader,
37
+ getApiKey: common.getApiKey,
37
38
  safeExecSync: common.safeExecSync,
38
39
  isCommandAvailable: common.isCommandAvailable,
39
40
  ensureDirectory: common.ensureDirectory,
@@ -18,6 +18,12 @@
18
18
  import fs from "node:fs";
19
19
  import path from "node:path";
20
20
  import os from "node:os";
21
+ import { fileURLToPath } from "node:url";
22
+ import { syncMcpUrl as syncTraeIde } from "./lib/trae-ide.js";
23
+ import { syncMcpUrl as syncClaudeCode } from "./lib/claude-code.js";
24
+ import { syncMcpUrl as syncQwenCode } from "./lib/qwen-code.js";
25
+ import { syncMcpUrl as syncQoder } from "./lib/qoder.js";
26
+ import { getMcpServerUrl, getMcpServerName } from "./lib/common.js";
21
27
 
22
28
  // ==================== 工具函数 ====================
23
29
 
@@ -86,6 +92,31 @@ function parseArgs(argv) {
86
92
  return args;
87
93
  }
88
94
 
95
+ // ==================== 同步 IDE 配置 ====================
96
+
97
+ function syncIdeConfigs() {
98
+ const serverUrl = getMcpServerUrl();
99
+ const serverName = getMcpServerName();
100
+ const results = [];
101
+
102
+ const syncFns = [
103
+ { name: "Trae CN", fn: syncTraeIde },
104
+ { name: "Claude Code", fn: syncClaudeCode },
105
+ { name: "Qwen Code", fn: syncQwenCode },
106
+ { name: "Qoder", fn: syncQoder },
107
+ ];
108
+
109
+ for (const { name, fn } of syncFns) {
110
+ try {
111
+ fn(serverUrl, serverName);
112
+ results.push(` ✓ ${name}`);
113
+ } catch (e) {
114
+ results.push(` ⏭️ ${name}: ${e.message}`);
115
+ }
116
+ }
117
+ return results;
118
+ }
119
+
89
120
  // ==================== 命令实现 ====================
90
121
 
91
122
  function cmdGet() {
@@ -116,7 +147,10 @@ function cmdSet(apiKey) {
116
147
  console.log(`\n✅ API Key 已保存。`);
117
148
  console.log(` MCP_API_KEY : ${old} → ${maskKey(apiKey)}`);
118
149
  console.log(` 配置文件 : ${getConfigFilePath()}`);
119
- console.log(`\n 提示:重启 IDE/CLI 后生效。\n`);
150
+ console.log(`\n 🔄 同步 IDE 配置...`);
151
+ const results = syncIdeConfigs();
152
+ for (const r of results) console.log(r);
153
+ console.log(`\n 提示:重启 IDE 后生效。\n`);
120
154
  return true;
121
155
  }
122
156
 
@@ -139,6 +173,9 @@ function cmdClear() {
139
173
  console.log(`\n✅ API Key 已清除。`);
140
174
  console.log(` MCP_API_KEY : ${old} → (未设置)`);
141
175
  console.log(` 配置文件 : ${getConfigFilePath()}`);
176
+ console.log(`\n 🔄 同步 IDE 配置...`);
177
+ const results = syncIdeConfigs();
178
+ for (const r of results) console.log(r);
142
179
  console.log(`\n 提示:服务端若已开启鉴权,清除后客户端将无法连接。\n`);
143
180
  return true;
144
181
  }
@@ -19,7 +19,7 @@ compatibility:
19
19
 
20
20
  **重要说明**:这是一个客户端 skill,通过 MCP 协议(SSE 或 HTTP)连接到远程 MCP 服务器。服务器由管理员维护,包含源码和数据。
21
21
 
22
- **版本**:v0.17.0 - 新增 10 段网段本地网络兼容:api_tool 新增 parse_swagger_json action,客户端新增 local-api-tool 技能,遇到 10.x.x.x 网段自动在本地 curl 获取 Swagger JSON 再交由服务端解析
22
+ **版本**:v0.18.0 - 新增 MCP 鉴权支持:postinstall 自动同步 API Key 到 IDE 配置,szcd-mcp-auth 命令支持 set/clear 并自动更新所有 IDE mcp.json
23
23
 
24
24
  ## 架构说明
25
25