luma-mcp 1.0.0 → 1.0.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/.claude/settings.local.json +10 -0
- package/README.md +4 -3
- package/build/index.js +9 -9
- package/build/index.js.map +1 -1
- package/mcp-server/README.md +41 -0
- package/mcp-server/README.zh-CN.md +42 -0
- package/mcp-server/build/core/api-common.js +122 -0
- package/mcp-server/build/core/chat-service.js +80 -0
- package/mcp-server/build/core/environment.js +128 -0
- package/mcp-server/build/core/error-handler.js +376 -0
- package/mcp-server/build/core/file-service.js +126 -0
- package/mcp-server/build/index.js +160 -0
- package/mcp-server/build/tools/image-analysis.js +125 -0
- package/mcp-server/build/tools/video-analysis.js +125 -0
- package/mcp-server/build/types/index.js +35 -0
- package/mcp-server/build/types/validation-types.js +1 -0
- package/mcp-server/build/utils/logger.js +120 -0
- package/mcp-server/build/utils/validation.js +198 -0
- package/mcp-server/package.json +53 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -137,9 +137,10 @@ claude mcp add -s user luma-mcp --env ZHIPU_API_KEY=your-api-key -- npx -y luma-
|
|
|
137
137
|
- Luma MCP 主要服务于**不支持视觉的模型**(如 GPT-4、Claude Opus 等文本模型)
|
|
138
138
|
|
|
139
139
|
**如何确保工具被调用**:
|
|
140
|
-
1.
|
|
141
|
-
2.
|
|
142
|
-
3.
|
|
140
|
+
1. 使用完整工具名:`使用 mcp__luma-mcp__analyze_image 工具分析这张图片`
|
|
141
|
+
2. 使用简化名称:`用 analyze_image 工具查看 ./screenshot.png`
|
|
142
|
+
3. 提供图片路径:`请用图片分析工具查看 ./screenshot.png 中的代码错误`
|
|
143
|
+
4. 明确提及服务器:`通过 luma-mcp 服务器分析这张图片`
|
|
143
144
|
|
|
144
145
|
**注意**: 直接在聊天框粘贴图片,非视觉模型不会自动调用 Luma,需要明确指示。
|
|
145
146
|
|
package/build/index.js
CHANGED
|
@@ -32,17 +32,17 @@ async function createServer() {
|
|
|
32
32
|
},
|
|
33
33
|
});
|
|
34
34
|
// 创建带重试的分析函数
|
|
35
|
-
const analyzeWithRetry = withRetry(async (imageSource,
|
|
35
|
+
const analyzeWithRetry = withRetry(async (imageSource, prompt) => {
|
|
36
36
|
// 1. 验证图片来源
|
|
37
37
|
await validateImageSource(imageSource);
|
|
38
38
|
// 2. 处理图片(读取或返回URL)
|
|
39
39
|
const imageDataUrl = await imageToBase64(imageSource);
|
|
40
|
-
// 3.
|
|
41
|
-
const
|
|
40
|
+
// 3. 构建提示词(直接使用prompt)
|
|
41
|
+
const fullPrompt = buildAnalysisPrompt(prompt);
|
|
42
42
|
// 4. 调用 GLM-4.5V 分析图片
|
|
43
|
-
return await zhipuClient.analyzeImage(imageDataUrl,
|
|
43
|
+
return await zhipuClient.analyzeImage(imageDataUrl, fullPrompt);
|
|
44
44
|
}, 2, // 最多重试2次
|
|
45
|
-
1000 //
|
|
45
|
+
1000 // 初始延补1秒
|
|
46
46
|
);
|
|
47
47
|
// 注册工具 - 使用 McpServer.tool() API
|
|
48
48
|
server.tool('analyze_image', `使用智谱GLM-4.5V视觉模型分析图片内容。
|
|
@@ -59,16 +59,16 @@ async function createServer() {
|
|
|
59
59
|
**支持来源**:本地文件、远程URL、临时文件(包括截图)。
|
|
60
60
|
|
|
61
61
|
如果你是不支持视觉的AI模型,看到图片路径时应主动调用此工具来分析图片内容。`, {
|
|
62
|
-
image_source: z.string().describe('
|
|
63
|
-
|
|
62
|
+
image_source: z.string().describe('图片来源:本地文件路径(含临时路径)、远程URL。支持 PNG/JPG/WebP/GIF。例如:"./image.png"、"/tmp/screenshot.png"、"C:\\Users\\...\\image.jpg"、"https://example.com/pic.jpg"'),
|
|
63
|
+
prompt: z.string().describe('必须:详细的分析指令。如果用户没有提供具体问题,默认使用:"请详细分析这张图片的内容"。对于具体任务:代码分析、UI设计、错误诊断、文字识别等,应提供明确的指令。例如:"这段代码为什么报错?"、"分析这个UI的布局和风格"、"识别图片中的所有文字"'),
|
|
64
64
|
}, async (params) => {
|
|
65
65
|
try {
|
|
66
66
|
logger.info('Analyzing image', {
|
|
67
67
|
source: params.image_source,
|
|
68
|
-
|
|
68
|
+
prompt: params.prompt,
|
|
69
69
|
});
|
|
70
70
|
// 执行分析(带重试)
|
|
71
|
-
const result = await analyzeWithRetry(params.image_source, params.
|
|
71
|
+
const result = await analyzeWithRetry(params.image_source, params.prompt);
|
|
72
72
|
logger.info('Image analysis completed successfully');
|
|
73
73
|
return createSuccessResponse(result);
|
|
74
74
|
}
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAEH,wCAAwC;AACxC,OAAO,EAAE,uBAAuB,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACpE,uBAAuB,EAAE,CAAC;AAE1B,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE3F;;GAEG;AACH,KAAK,UAAU,YAAY;IACzB,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IAE5C,OAAO;IACP,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IAE5C,uBAAuB;IACvB,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;QACE,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,OAAO;KACjB,EACD;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE;SACV;KACF,CACF,CAAC;IAEF,aAAa;IACb,MAAM,gBAAgB,GAAG,SAAS,CAChC,KAAK,EAAE,WAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# ZAI MCP Server
|
|
2
|
+
|
|
3
|
+
[中文文档](https://docs.bigmodel.cn/cn/coding-plan/mcp/vision-mcp-server) | [English Document](https://docs.z.ai/devpack/mcp/vision-mcp-server)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
A Model Context Protocol (MCP) server that provides AI capabilities powered by Z.AI.
|
|
7
|
+
|
|
8
|
+
## Environment Variables
|
|
9
|
+
|
|
10
|
+
- `Z_AI_MODE` - The platform to use for AI services (default: `ZHIPU`) ZHIPU or ZAI
|
|
11
|
+
- `Z_AI_API_KEY` - Your API key
|
|
12
|
+
|
|
13
|
+
For ZAI: Use ZAI platform https://z.ai/model-api
|
|
14
|
+
|
|
15
|
+
For ZHIPU: Use Zhipu AI platform https://bigmodel.cn
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Use this MCP Server in Claude Code
|
|
20
|
+
|
|
21
|
+
It is recommended to use the version Node.js 18+, Claude Code 2.0.14+.
|
|
22
|
+
|
|
23
|
+
**Note**: You need to configure `Z_AI_MODE` as `ZAI` or `ZHIPU` depending on the platform.
|
|
24
|
+
|
|
25
|
+
For ZAI Use,
|
|
26
|
+
|
|
27
|
+
```shell
|
|
28
|
+
claude mcp add zai-mcp-server --env Z_AI_API_KEY=your_api_key Z_AI_MODE=ZAI -- npx -y "@z_ai/mcp-server"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For ZHIPU Use,
|
|
32
|
+
|
|
33
|
+
```shell
|
|
34
|
+
claude mcp add zai-mcp-server --env Z_AI_API_KEY=your_api_key Z_AI_MODE=ZHIPU -- npx -y "@z_ai/mcp-server"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Use this MCP Server in Others MCP Client
|
|
38
|
+
|
|
39
|
+
For Z.ai Use, refer the [Vision MCP Doc](https://docs.z.ai/devpack/mcp/vision-mcp-server)
|
|
40
|
+
|
|
41
|
+
For ZhiPU Use, refer the [Vision MCP Doc](https://docs.bigmodel.cn/cn/coding-plan/mcp/vision-mcp-server)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# ZAI MCP 服务器
|
|
2
|
+
|
|
3
|
+
[中文文档](https://docs.bigmodel.cn/cn/coding-plan/mcp/vision-mcp-server) | [English Document](https://docs.z.ai/devpack/mcp/vision-mcp-server)
|
|
4
|
+
|
|
5
|
+
一个基于 Z.AI 提供 AI 能力的模型上下文协议 (MCP) 服务器。
|
|
6
|
+
|
|
7
|
+
## MCP 工具
|
|
8
|
+
|
|
9
|
+
该服务器实现了模型上下文协议,可与任何兼容 MCP 的客户端一起使用。服务器提供以下工具:
|
|
10
|
+
|
|
11
|
+
- `image_analysis` - 分析图像并提供详细描述
|
|
12
|
+
- `video_analysis` - 分析视频并提供详细描述
|
|
13
|
+
|
|
14
|
+
## 环境变量
|
|
15
|
+
|
|
16
|
+
- `Z_AI_MODE` - 用于 AI 服务的平台(默认:`ZHIPU`)ZHIPU 或 ZAI
|
|
17
|
+
- `Z_AI_API_KEY` - 您的 API 密钥
|
|
18
|
+
|
|
19
|
+
对于 `Z_AI_MODE=ZHIPU`:使用智谱 AI 平台 https://bigmodel.cn
|
|
20
|
+
|
|
21
|
+
对于 `Z_AI_MODE=ZAI`:使用 ZAI 平台 https://z.ai/model-api
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## 安装
|
|
25
|
+
|
|
26
|
+
### 从 npm 安装
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm i @z_ai/mcp-server
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 使用方法
|
|
33
|
+
|
|
34
|
+
### 在 Claude Code 中使用此 MCP 服务器
|
|
35
|
+
|
|
36
|
+
```shell
|
|
37
|
+
claude mcp add zai-mcp-server --env Z_AI_API_KEY=your_api_key -- npx -y "@z_ai/mcp-server"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 在其他客户端中使用此 MCP 服务器
|
|
41
|
+
|
|
42
|
+
参考文档 [视觉理解 MCP](https://docs.bigmodel.cn/cn/coding-plan/mcp/vision-mcp-server)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create multimodal message content
|
|
3
|
+
* @param content Content array including images, text, etc.
|
|
4
|
+
* @param prompt Text prompt
|
|
5
|
+
* @returns Formatted message array
|
|
6
|
+
*/
|
|
7
|
+
export function createMultiModalMessage(content, prompt) {
|
|
8
|
+
return [{
|
|
9
|
+
role: 'user',
|
|
10
|
+
content: [...content, { type: 'text', text: prompt }]
|
|
11
|
+
}];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create text message
|
|
15
|
+
* @param prompt Text content
|
|
16
|
+
* @returns Formatted message array
|
|
17
|
+
*/
|
|
18
|
+
export function createTextMessage(prompt) {
|
|
19
|
+
return [{
|
|
20
|
+
role: 'user',
|
|
21
|
+
content: [{ type: 'text', text: prompt }]
|
|
22
|
+
}];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Create image message content
|
|
26
|
+
* @param imageUrl Image URL or base64 data
|
|
27
|
+
* @returns Image content object
|
|
28
|
+
*/
|
|
29
|
+
export function createImageContent(imageUrl) {
|
|
30
|
+
return {
|
|
31
|
+
type: 'image_url',
|
|
32
|
+
image_url: { url: imageUrl }
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create video message content
|
|
37
|
+
* @param videoUrl Video URL or base64 data
|
|
38
|
+
* @returns Video content object
|
|
39
|
+
*/
|
|
40
|
+
export function createVideoContent(videoUrl) {
|
|
41
|
+
return {
|
|
42
|
+
type: 'video_url', // Most AI models treat video as image_url type
|
|
43
|
+
video_url: { url: videoUrl }
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create error response
|
|
48
|
+
* @param message Error message
|
|
49
|
+
* @param error Optional error object
|
|
50
|
+
* @returns Standardized error response
|
|
51
|
+
*/
|
|
52
|
+
export function createErrorResponse(message, error) {
|
|
53
|
+
return {
|
|
54
|
+
success: false,
|
|
55
|
+
error: message,
|
|
56
|
+
timestamp: Date.now(),
|
|
57
|
+
...(error && { context: { stack: error.stack, name: error.name } })
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create success response
|
|
62
|
+
* @param data Response data
|
|
63
|
+
* @returns Standardized success response
|
|
64
|
+
*/
|
|
65
|
+
export function createSuccessResponse(data) {
|
|
66
|
+
return {
|
|
67
|
+
success: true,
|
|
68
|
+
data,
|
|
69
|
+
timestamp: Date.now()
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Format response content to MCP format
|
|
74
|
+
* @param response API response
|
|
75
|
+
* @returns MCP tool response format
|
|
76
|
+
*/
|
|
77
|
+
export function formatMcpResponse(response) {
|
|
78
|
+
if (response.success) {
|
|
79
|
+
const text = typeof response.data === 'string'
|
|
80
|
+
? response.data
|
|
81
|
+
: JSON.stringify(response.data, null, 2);
|
|
82
|
+
return {
|
|
83
|
+
content: [{ type: 'text', text }]
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
return {
|
|
88
|
+
content: [{
|
|
89
|
+
type: 'text',
|
|
90
|
+
text: `Error: ${response.error}`
|
|
91
|
+
}],
|
|
92
|
+
isError: true
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Create async function with retry mechanism
|
|
98
|
+
* @param fn Async function to execute
|
|
99
|
+
* @param maxRetries Maximum retry attempts
|
|
100
|
+
* @param delay Retry delay in milliseconds
|
|
101
|
+
* @returns Wrapped function
|
|
102
|
+
*/
|
|
103
|
+
export function withRetry(fn, maxRetries = 3, delay = 1000) {
|
|
104
|
+
return async (...args) => {
|
|
105
|
+
let lastError;
|
|
106
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
107
|
+
try {
|
|
108
|
+
return await fn(...args);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
112
|
+
if (attempt === maxRetries) {
|
|
113
|
+
throw lastError;
|
|
114
|
+
}
|
|
115
|
+
// Exponential backoff
|
|
116
|
+
const waitTime = delay * Math.pow(2, attempt);
|
|
117
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
throw lastError;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ApiError } from '../types/index.js';
|
|
2
|
+
import { configurationService } from './environment.js';
|
|
3
|
+
import { EnvironmentService } from './environment.js';
|
|
4
|
+
/**
|
|
5
|
+
* ZAI API service implementation
|
|
6
|
+
*/
|
|
7
|
+
export class ChatService {
|
|
8
|
+
environmentService;
|
|
9
|
+
constructor(environmentService = EnvironmentService.getInstance()) {
|
|
10
|
+
this.environmentService = environmentService;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* ZAI chat completions API for vision analysis
|
|
14
|
+
*/
|
|
15
|
+
async visionCompletions(messages) {
|
|
16
|
+
const visionConfig = configurationService.getVisionConfig();
|
|
17
|
+
const requestBody = {
|
|
18
|
+
model: visionConfig.model,
|
|
19
|
+
messages,
|
|
20
|
+
thinking: { type: 'enabled' },
|
|
21
|
+
stream: false,
|
|
22
|
+
temperature: visionConfig.temperature,
|
|
23
|
+
top_p: visionConfig.topP,
|
|
24
|
+
max_tokens: visionConfig.maxTokens
|
|
25
|
+
};
|
|
26
|
+
console.info('Request ZAI chat completions API for vision analysis', { model: visionConfig.model, messageCount: messages.length });
|
|
27
|
+
try {
|
|
28
|
+
const response = await this.chatCompletions(visionConfig.url, requestBody);
|
|
29
|
+
const result = response.choices?.[0]?.message?.content;
|
|
30
|
+
if (!result) {
|
|
31
|
+
throw new ApiError('Invalid API response: missing content');
|
|
32
|
+
}
|
|
33
|
+
console.info('Request chat completions API for vision analysis successful');
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error('Request chat completions API for vision analysis failed', { error: error instanceof Error ? error.message : String(error) });
|
|
38
|
+
throw error instanceof ApiError ? error : new ApiError(`API call failed: ${error}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Make HTTP request to ZAI API with proper headers and error handling
|
|
43
|
+
*/
|
|
44
|
+
async chatCompletions(url, body) {
|
|
45
|
+
const apiConfig = configurationService.getVisionConfig();
|
|
46
|
+
const apiKey = this.environmentService.getApiKey();
|
|
47
|
+
const controller = new AbortController();
|
|
48
|
+
const timeoutId = setTimeout(() => controller.abort(), apiConfig.timeout);
|
|
49
|
+
try {
|
|
50
|
+
const response = await fetch(url, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: {
|
|
53
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'X-Title': '4.5V MCP Local',
|
|
56
|
+
'Accept-Language': 'en-US,en'
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify(body),
|
|
59
|
+
signal: controller.signal
|
|
60
|
+
});
|
|
61
|
+
clearTimeout(timeoutId);
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
const errorText = await response.text();
|
|
64
|
+
throw new ApiError(`HTTP ${response.status}: ${errorText}`);
|
|
65
|
+
}
|
|
66
|
+
return await response.json();
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
clearTimeout(timeoutId);
|
|
70
|
+
if (error instanceof ApiError) {
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
throw new ApiError(`Network error: ${error}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* ZAI API chat completions service instance
|
|
79
|
+
*/
|
|
80
|
+
export const chatService = new ChatService();
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { ApiError } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Environment configuration service using singleton pattern
|
|
4
|
+
*/
|
|
5
|
+
export class EnvironmentService {
|
|
6
|
+
static instance;
|
|
7
|
+
config = null;
|
|
8
|
+
constructor() { }
|
|
9
|
+
/**
|
|
10
|
+
* Get singleton instance of EnvironmentService
|
|
11
|
+
*/
|
|
12
|
+
static getInstance() {
|
|
13
|
+
if (!EnvironmentService.instance) {
|
|
14
|
+
EnvironmentService.instance = new EnvironmentService();
|
|
15
|
+
}
|
|
16
|
+
return EnvironmentService.instance;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get environment configuration
|
|
20
|
+
*/
|
|
21
|
+
getConfig() {
|
|
22
|
+
if (!this.config) {
|
|
23
|
+
this.config = this.loadEnvironmentConfig();
|
|
24
|
+
}
|
|
25
|
+
return this.config;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Load environment configuration from process.env
|
|
29
|
+
*/
|
|
30
|
+
loadEnvironmentConfig() {
|
|
31
|
+
const envConfig = { ...process.env };
|
|
32
|
+
if (!envConfig.Z_AI_BASE_URL) {
|
|
33
|
+
// for z.ai paas is https://api.z.ai/api/paas/v4/
|
|
34
|
+
// for zhipuai is https://open.bigmodel.cn/api/paas/v4/
|
|
35
|
+
envConfig.Z_AI_BASE_URL = 'https://open.bigmodel.cn/api/paas/v4/';
|
|
36
|
+
}
|
|
37
|
+
if (envConfig.PLATFORM_MODE != null) {
|
|
38
|
+
console.info('Running in mode', { mode: envConfig.PLATFORM_MODE });
|
|
39
|
+
if (envConfig.PLATFORM_MODE === 'Z_AI' || envConfig.PLATFORM_MODE === 'ZAI' || envConfig.PLATFORM_MODE === 'Z') {
|
|
40
|
+
envConfig.Z_AI_BASE_URL = 'https://api.z.ai/api/paas/v4/';
|
|
41
|
+
envConfig.PLATFORM_MODE = 'ZAI';
|
|
42
|
+
}
|
|
43
|
+
else if (envConfig.PLATFORM_MODE === 'ZHIPU_AI' || envConfig.PLATFORM_MODE === 'ZHIPUAI'
|
|
44
|
+
|| envConfig.PLATFORM_MODE === 'ZHIPU' || envConfig.PLATFORM_MODE === 'BIGMODEL') {
|
|
45
|
+
envConfig.Z_AI_BASE_URL = 'https://open.bigmodel.cn/api/paas/v4/';
|
|
46
|
+
envConfig.PLATFORM_MODE = 'ZHIPU';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
envConfig.PLATFORM_MODE = 'ZHIPU';
|
|
51
|
+
}
|
|
52
|
+
if (!envConfig.Z_AI_API_KEY && envConfig.ZAI_API_KEY) {
|
|
53
|
+
envConfig.Z_AI_API_KEY = envConfig.ZAI_API_KEY;
|
|
54
|
+
console.warn("[important] Z_AI_API_KEY is not set but found ZAI_API_KEY, using ZAI_API_KEY as Z_AI_API_KEY");
|
|
55
|
+
}
|
|
56
|
+
// for some user forget replace the `your_api_key` `your_zhipu_api_key` `your_zai_api_key` in the env
|
|
57
|
+
if (!envConfig.Z_AI_API_KEY || envConfig.Z_AI_API_KEY?.toLowerCase().includes('api')
|
|
58
|
+
|| envConfig.Z_AI_API_KEY?.toLowerCase().includes('key')) {
|
|
59
|
+
if (envConfig.ANTHROPIC_AUTH_TOKEN && !envConfig.ANTHROPIC_AUTH_TOKEN?.toLowerCase().includes('api')) {
|
|
60
|
+
// use the ANTHROPIC_AUTH_TOKEN as Z_AI_API_KEY if available
|
|
61
|
+
envConfig.Z_AI_API_KEY = envConfig.ANTHROPIC_AUTH_TOKEN;
|
|
62
|
+
console.warn('[important] Z_AI_API_KEY is not set but found ANTHROPIC_AUTH_TOKEN, using ANTHROPIC_AUTH_TOKEN as Z_AI_API_KEY');
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
throw new ApiError('Z_AI_API_KEY environment variable is required, please set your actual API key');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
Z_AI_BASE_URL: envConfig.Z_AI_BASE_URL,
|
|
70
|
+
Z_AI_API_KEY: envConfig.Z_AI_API_KEY,
|
|
71
|
+
Z_AI_VISION_MODEL: envConfig.Z_AI_VISION_MODEL,
|
|
72
|
+
Z_AI_VISION_MODEL_TEMPERATURE: envConfig.Z_AI_VISION_MODEL_TEMPERATURE,
|
|
73
|
+
Z_AI_VISION_MODEL_TOP_P: envConfig.Z_AI_VISION_MODEL_TOP_P,
|
|
74
|
+
Z_AI_VISION_MODEL_MAX_TOKENS: envConfig.Z_AI_VISION_MODEL_MAX_TOKENS,
|
|
75
|
+
Z_AI_TIMEOUT: envConfig.Z_AI_TIMEOUT,
|
|
76
|
+
Z_AI_RETRY_COUNT: envConfig.Z_AI_RETRY_COUNT,
|
|
77
|
+
SERVER_NAME: envConfig.SERVER_NAME,
|
|
78
|
+
SERVER_VERSION: envConfig.SERVER_VERSION,
|
|
79
|
+
PLATFORM_MODE: envConfig.PLATFORM_MODE
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get server configuration
|
|
84
|
+
*/
|
|
85
|
+
getServerConfig() {
|
|
86
|
+
const config = this.getConfig();
|
|
87
|
+
return {
|
|
88
|
+
name: config.SERVER_NAME || 'zai-mcp-server',
|
|
89
|
+
version: config.SERVER_VERSION || '0.1.0'
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get platform mode
|
|
94
|
+
*/
|
|
95
|
+
getPlatformModel() {
|
|
96
|
+
const config = this.getConfig();
|
|
97
|
+
return config.PLATFORM_MODE || 'ZHIPU';
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get API configuration
|
|
101
|
+
*/
|
|
102
|
+
getVisionConfig() {
|
|
103
|
+
const config = this.getConfig();
|
|
104
|
+
return {
|
|
105
|
+
model: config.Z_AI_VISION_MODEL || 'glm-4.5v',
|
|
106
|
+
timeout: parseInt(config.Z_AI_TIMEOUT || '300000'),
|
|
107
|
+
retryCount: parseInt(config.Z_AI_RETRY_COUNT || '1'),
|
|
108
|
+
url: config.Z_AI_BASE_URL + 'chat/completions',
|
|
109
|
+
temperature: parseFloat(config.Z_AI_VISION_MODEL_TEMPERATURE || '0.8'),
|
|
110
|
+
topP: parseFloat(config.Z_AI_VISION_MODEL_TOP_P || '0.6'),
|
|
111
|
+
maxTokens: parseInt(config.Z_AI_VISION_MODEL_MAX_TOKENS || '16384')
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get ZAI API key from configuration
|
|
116
|
+
*/
|
|
117
|
+
getApiKey() {
|
|
118
|
+
return this.getConfig().Z_AI_API_KEY;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Global environment service instance
|
|
123
|
+
*/
|
|
124
|
+
export const environmentService = EnvironmentService.getInstance();
|
|
125
|
+
/**
|
|
126
|
+
* Configuration service instance (for backward compatibility)
|
|
127
|
+
*/
|
|
128
|
+
export const configurationService = environmentService;
|