@codify-ai/mcp-client 1.0.0 → 1.0.2

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.
Files changed (2) hide show
  1. package/mcp-client.js +185 -55
  2. package/package.json +12 -1
package/mcp-client.js CHANGED
@@ -6,25 +6,26 @@
6
6
  *
7
7
  * 使用方法:
8
8
  * 在 Cursor 配置 (~/.cursor/mcp.json) 中:
9
- * {
10
- * "mcpServers": {
11
- * "codify": {
12
- * "command": "npx",
13
- * "args": ["-y", "@codify/mcp-client", "--url=http://your-server:8080"],
14
- * "env": {
15
- * "CODIFY_ACCESS_KEY": "sk-your-access-key"
16
- * }
17
- * }
18
- * }
19
- * }
9
+ {
10
+ "mcpServers": {
11
+ "codify": {
12
+ "command": "npx",
13
+ "args": ["-y", "@codify/mcp-client", "--url=http://your-server:8080"],
14
+ "env": {
15
+ "CODIFY_ACCESS_KEY": "sk-your-access-key"
16
+ }
17
+ }
18
+ }
19
+ }
20
20
  */
21
21
 
22
22
  import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
23
23
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
24
+ import { z } from 'zod';
24
25
 
25
26
  function parseArgs() {
26
27
  const args = process.argv.slice(2);
27
- let serverUrl = process.env.CODIFY_SERVER_URL || 'http://localhost:8080';
28
+ let serverUrl = process.env.CODIFY_SERVER_URL || 'https://mcp.codify-api.com';
28
29
 
29
30
  for (let i = 0; i < args.length; i++) {
30
31
  const arg = args[i];
@@ -150,7 +151,42 @@ mcpServer.registerResource(
150
151
  async (uri, variables) => {
151
152
  try {
152
153
  // 从 variables 中获取 channelId(由 SDK 解析)
153
- const channelId = variables.channelId || uri.hostname;
154
+ // 如果 variables 为空或没有 channelId,尝试从 URI 解析
155
+ let channelId = variables?.channelId;
156
+
157
+ if (!channelId) {
158
+ // 尝试从 URI hostname 获取(codify://my-demo/current -> my-demo)
159
+ channelId = uri.hostname;
160
+ }
161
+
162
+ // 如果还是为空,尝试从 pathname 解析(备用方案)
163
+ if (!channelId && uri.pathname) {
164
+ const pathParts = uri.pathname.split('/').filter(p => p);
165
+ if (pathParts.length > 0) {
166
+ channelId = pathParts[0];
167
+ }
168
+ }
169
+
170
+ if (!channelId) {
171
+ console.error(`❌ 无法从 URI 解析 channelId: ${uri.href}`);
172
+ console.error(` variables:`, variables);
173
+ return {
174
+ contents: [{
175
+ uri: uri.href,
176
+ text: `// 错误: 无法解析频道 ID\n// URI: ${uri.href}\n// 请使用格式: codify://channelId/current`
177
+ }]
178
+ };
179
+ }
180
+
181
+ if (process.env.DEBUG) {
182
+ console.error(`[DEBUG] 获取资源: ${uri.href}`);
183
+ console.error(`[DEBUG] variables:`, variables);
184
+ console.error(`[DEBUG] uri.hostname:`, uri.hostname);
185
+ console.error(`[DEBUG] uri.pathname:`, uri.pathname);
186
+ console.error(`[DEBUG] 解析的 channelId: ${channelId}`);
187
+ console.error(`[DEBUG] SERVER_URL: ${SERVER_URL}`);
188
+ console.error(`[DEBUG] ACCESS_KEY: ${ACCESS_KEY ? ACCESS_KEY.substring(0, 10) + '...' : '未设置'}`);
189
+ }
154
190
 
155
191
  // 添加认证头
156
192
  const headers = {};
@@ -159,45 +195,109 @@ mcpServer.registerResource(
159
195
  }
160
196
 
161
197
  // 通过 HTTP API 从远程服务器获取数据
162
- const response = await fetch(`${SERVER_URL}/api/channel/${channelId}`, { headers });
198
+ const apiUrl = `${SERVER_URL}/api/channel/${channelId}`;
199
+ if (process.env.DEBUG) {
200
+ console.error(`[DEBUG] 请求 URL: ${apiUrl}`);
201
+ }
202
+
203
+ let response;
204
+ try {
205
+ response = await fetch(apiUrl, { headers });
206
+ } catch (fetchError) {
207
+ const errorMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
208
+ console.error(`❌ 网络请求失败: ${errorMsg}`);
209
+ console.error(` 服务器 URL: ${SERVER_URL}`);
210
+ console.error(` 请检查: 1) 服务器是否运行 2) URL 是否正确 3) 网络连接`);
211
+ return {
212
+ contents: [{
213
+ uri: uri.href,
214
+ text: `// 网络错误: ${errorMsg}\n// 服务器: ${SERVER_URL}\n// 请检查服务器地址和网络连接`
215
+ }]
216
+ };
217
+ }
163
218
 
164
219
  if (!response.ok) {
165
- // 如果 API 返回错误,尝试从 channels 列表中查找
166
- const channelsResponse = await fetch(`${SERVER_URL}/channels`, { headers });
167
-
168
- if (!channelsResponse.ok) {
169
- return {
170
- contents: [{
171
- uri: uri.href,
172
- text: `// 认证失败或服务器错误 (${channelsResponse.status})`
173
- }]
174
- };
175
- }
176
-
177
- const channels = await channelsResponse.json();
178
- const channel = channels.find(ch => ch.channelId === channelId);
220
+ const errorText = await response.text().catch(() => '无法读取错误信息');
221
+ console.error(`❌ API 请求失败: ${response.status} ${response.statusText}`);
222
+ console.error(` 错误详情: ${errorText}`);
179
223
 
180
- if (!channel) {
224
+ // 如果 API 返回错误,尝试从 channels 列表中查找
225
+ try {
226
+ const channelsResponse = await fetch(`${SERVER_URL}/channels`, { headers });
227
+
228
+ if (!channelsResponse.ok) {
229
+ const channelsErrorText = await channelsResponse.text().catch(() => '无法读取错误信息');
230
+ console.error(`❌ 获取频道列表失败: ${channelsResponse.status}`);
231
+ console.error(` 错误详情: ${channelsErrorText}`);
232
+ return {
233
+ contents: [{
234
+ uri: uri.href,
235
+ text: `// 认证失败或服务器错误 (${channelsResponse.status})\n// 错误: ${channelsErrorText}\n// 请检查 CODIFY_ACCESS_KEY 是否正确`
236
+ }]
237
+ };
238
+ }
239
+
240
+ const channels = await channelsResponse.json();
241
+ const channel = channels.find(ch => ch.channelId === channelId);
242
+
243
+ if (!channel) {
244
+ return {
245
+ contents: [{
246
+ uri: uri.href,
247
+ text: `// 通道 ${channelId} 不存在或为空\n// 可用通道: ${channels.map(ch => ch.channelId).join(', ') || '无'}`
248
+ }]
249
+ };
250
+ }
251
+
252
+ // 通道存在但第一次获取失败,再次尝试获取代码
253
+ console.error(`⚠️ 第一次获取失败,但通道存在,再次尝试获取代码...`);
254
+ try {
255
+ const retryResponse = await fetch(`${SERVER_URL}/api/channel/${channelId}`, { headers });
256
+ if (retryResponse.ok) {
257
+ const retryData = await retryResponse.json();
258
+ console.log(`✅ 重试成功,获取到代码`);
259
+ return {
260
+ contents: [{
261
+ uri: uri.href,
262
+ text: retryData.code || "// 等待 Figma WebSocket..."
263
+ }]
264
+ };
265
+ } else {
266
+ const retryErrorText = await retryResponse.text().catch(() => '无法读取错误信息');
267
+ console.error(`❌ 重试仍然失败: ${retryResponse.status}`);
268
+ return {
269
+ contents: [{
270
+ uri: uri.href,
271
+ text: `// 通道 ${channelId} 存在但无法获取代码 (${retryResponse.status})\n// 代码长度: ${channel.codeLength} 字符\n// 最后更新: ${channel.lastUpdate}\n// 错误: ${retryErrorText}`
272
+ }]
273
+ };
274
+ }
275
+ } catch (retryError) {
276
+ console.error(`❌ 重试请求异常:`, retryError);
277
+ return {
278
+ contents: [{
279
+ uri: uri.href,
280
+ text: `// 通道 ${channelId} 存在但获取代码失败\n// 代码长度: ${channel.codeLength} 字符\n// 最后更新: ${channel.lastUpdate}\n// 错误: ${retryError instanceof Error ? retryError.message : String(retryError)}`
281
+ }]
282
+ };
283
+ }
284
+ } catch (channelsError) {
285
+ console.error(`❌ 获取频道列表异常:`, channelsError);
181
286
  return {
182
287
  contents: [{
183
288
  uri: uri.href,
184
- text: `// 通道 ${channelId} 不存在或为空`
289
+ text: `// 获取频道列表失败: ${channelsError instanceof Error ? channelsError.message : String(channelsError)}\n// 原始错误: ${response.status} ${errorText}`
185
290
  }]
186
291
  };
187
292
  }
188
-
189
- // 需要从完整数据中获取代码
190
- // 这里返回一个提示,因为我们没有直接的 API
191
- return {
192
- contents: [{
193
- uri: uri.href,
194
- text: `// 通道 ${channelId} 存在\n// 代码长度: ${channel.codeLength} 字符\n// 最后更新: ${channel.lastUpdate}`
195
- }]
196
- };
197
293
  }
198
294
 
199
295
  const data = await response.json();
200
296
 
297
+ if (process.env.DEBUG) {
298
+ console.error(`[DEBUG] 成功获取代码,长度: ${data.code?.length || 0} 字符`);
299
+ }
300
+
201
301
  return {
202
302
  contents: [{
203
303
  uri: uri.href,
@@ -205,11 +305,15 @@ mcpServer.registerResource(
205
305
  }]
206
306
  };
207
307
  } catch (error) {
208
- console.error(`❌ 获取资源失败:`, error);
308
+ const errorMsg = error instanceof Error ? error.message : String(error);
309
+ console.error(`❌ 获取资源失败:`, errorMsg);
310
+ if (error.stack && process.env.DEBUG) {
311
+ console.error(` 堆栈:`, error.stack);
312
+ }
209
313
  return {
210
314
  contents: [{
211
315
  uri: uri.href,
212
- text: `// 错误: ${error.message}`
316
+ text: `// 错误: ${errorMsg}\n// URI: ${uri.href}\n// 服务器: ${SERVER_URL}`
213
317
  }]
214
318
  };
215
319
  }
@@ -222,19 +326,36 @@ mcpServer.registerTool(
222
326
  {
223
327
  description: "从 Codify 服务器获取指定频道的代码",
224
328
  inputSchema: {
225
- type: "object",
226
- properties: {
227
- channelId: {
228
- type: "string",
229
- description: "频道 ID,例如: my-demo, test-channel"
230
- }
231
- },
232
- required: ["channelId"]
329
+ channelId: z.string().describe("频道 ID,例如: my-demo, test-channel")
233
330
  }
234
331
  },
235
- async ({ channelId }) => {
332
+ async (args) => {
236
333
  try {
334
+ // 调试:打印所有参数
335
+ if (process.env.DEBUG) {
336
+ console.error(`[DEBUG] getCode 接收到的参数:`, JSON.stringify(args, null, 2));
337
+ console.error(`[DEBUG] args 类型:`, typeof args);
338
+ console.error(`[DEBUG] args 是否为对象:`, args && typeof args === 'object');
339
+ console.error(`[DEBUG] args 的键:`, args ? Object.keys(args) : 'null');
340
+ }
341
+
342
+ // 从参数中获取 channelId
343
+ const channelId = args?.channelId;
344
+
237
345
  console.error(`[Tool] getCode 被调用: channelId=${channelId}`);
346
+ console.error(`[Tool] 原始 args:`, args);
347
+
348
+ if (!channelId) {
349
+ return {
350
+ content: [
351
+ {
352
+ type: "text",
353
+ text: `❌ 参数错误: 未提供 channelId\n\n💡 请使用格式: getCode({ channelId: "my-demo" })\n\n📋 调试信息:\n- args: ${JSON.stringify(args)}\n- args 类型: ${typeof args}`
354
+ }
355
+ ],
356
+ isError: true
357
+ };
358
+ }
238
359
 
239
360
  // 添加认证头
240
361
  const headers = {};
@@ -298,7 +419,8 @@ mcpServer.registerTool(
298
419
  mcpServer.registerTool(
299
420
  "listChannels",
300
421
  {
301
- description: "列出 Codify 服务器上所有可用的频道"
422
+ description: "列出 Codify 服务器上所有可用的频道",
423
+ inputSchema: {}
302
424
  },
303
425
  async () => {
304
426
  try {
@@ -358,7 +480,15 @@ mcpServer.registerTool(
358
480
  );
359
481
 
360
482
  // 连接 stdio 传输
361
- const transport = new StdioServerTransport();
362
- await mcpServer.connect(transport);
363
-
364
- console.error('✅ MCP 客户端已启动');
483
+ try {
484
+ const transport = new StdioServerTransport();
485
+ await mcpServer.connect(transport);
486
+ console.error('✅ MCP 客户端已启动');
487
+ } catch (error) {
488
+ console.error('❌ MCP 客户端启动失败:', error.message);
489
+ console.error('💡 请检查:');
490
+ console.error(' 1. 服务器地址是否正确:', SERVER_URL);
491
+ console.error(' 2. 服务器是否正在运行');
492
+ console.error(' 3. 网络连接是否正常');
493
+ process.exit(1);
494
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codify-ai/mcp-client",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Codify MCP 客户端 - 连接到远程 Codify MCP 服务器,供 Cursor 等 IDE 使用",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,6 +15,17 @@
15
15
  "node": ">=18.0.0",
16
16
  "npm": ">=8.0.0"
17
17
  },
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "start": "node dist/index.js",
21
+ "dev": "tsx watch src/index.ts",
22
+ "clean": "rm -rf dist",
23
+ "typecheck": "tsc --noEmit",
24
+ "prepare": "npm run build",
25
+ "mcp-client": "node mcp-client.js",
26
+ "publish:client": "bash script/publish.sh",
27
+ "build:deploy": "bash script/build-deploy.sh"
28
+ },
18
29
  "keywords": [
19
30
  "mcp",
20
31
  "model-context-protocol",