luma-mcp 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.
@@ -0,0 +1,125 @@
1
+ import { z } from 'zod';
2
+ import { FileNotFoundError, ApiError } from '../types/index.js';
3
+ import { ToolExecutionError } from '../core/error-handler.js';
4
+ import { CommonSchemas, ToolSchemaBuilder } from '../utils/validation.js';
5
+ import { createMultiModalMessage, createImageContent, formatMcpResponse, createSuccessResponse, createErrorResponse, withRetry } from '../core/api-common.js';
6
+ import { fileService } from '../core/file-service.js';
7
+ import { chatService } from '../core/chat-service.js';
8
+ /**
9
+ * Image analysis service class
10
+ */
11
+ export class ImageAnalysisService {
12
+ chatService = chatService;
13
+ fileService = fileService;
14
+ MAX_IMAGE_SIZE_MB = 5;
15
+ /**
16
+ * Execute image analysis
17
+ * @param request Image analysis request
18
+ * @returns Analysis result
19
+ */
20
+ async analyzeImage(request) {
21
+ console.info('Starting image analysis', {
22
+ imageSource: request.imageSource,
23
+ prompt: request.prompt
24
+ });
25
+ try {
26
+ // Validate image source (file or URL) and size
27
+ await this.fileService.validateImageSource(request.imageSource, this.MAX_IMAGE_SIZE_MB);
28
+ // Validate prompt
29
+ if (!request.prompt || request.prompt.trim().length === 0) {
30
+ throw new ToolExecutionError('Prompt is required for image analysis', 'image-analysis', 'VALIDATION_ERROR', {
31
+ toolName: 'image-analysis',
32
+ operation: 'analyzeImage',
33
+ metadata: { imageSource: request.imageSource }
34
+ });
35
+ }
36
+ // Handle image source (URL or local file)
37
+ let imageContent;
38
+ if (this.fileService.isUrl(request.imageSource)) {
39
+ // For URLs, pass directly without base64 encoding
40
+ imageContent = createImageContent(request.imageSource);
41
+ }
42
+ else {
43
+ // For local files, encode to base64
44
+ const imageData = await this.fileService.encodeImageToBase64(request.imageSource);
45
+ imageContent = createImageContent(imageData);
46
+ }
47
+ // Create multimodal message
48
+ const messages = createMultiModalMessage([imageContent], request.prompt);
49
+ const result = await this.chatService.visionCompletions(messages);
50
+ console.info('Image analysis completed', {
51
+ imageSource: request.imageSource
52
+ });
53
+ return result;
54
+ }
55
+ catch (error) {
56
+ console.error('Image analysis failed', {
57
+ error: error instanceof Error ? error.message : String(error),
58
+ imageSource: request.imageSource
59
+ });
60
+ if (error instanceof ToolExecutionError) {
61
+ throw error;
62
+ }
63
+ // Wrap unknown errors
64
+ throw new ToolExecutionError(`Image analysis failed: ${error.message}`, 'image-analysis', 'EXECUTION_ERROR', {
65
+ toolName: 'image-analysis',
66
+ operation: 'analyzeImage',
67
+ metadata: { imageSource: request.imageSource, originalError: error }
68
+ }, error);
69
+ }
70
+ }
71
+ }
72
+ /**
73
+ * Register image analysis tool with MCP server
74
+ * @param server MCP server instance
75
+ */
76
+ export function registerImageAnalysisTool(server) {
77
+ const analysisService = new ImageAnalysisService();
78
+ const retryableAnalyze = withRetry(analysisService.analyzeImage.bind(analysisService), 2, // Maximum 2 retries
79
+ 1000 // 1 second delay
80
+ );
81
+ server.tool('analyze_image', 'Analyze an image using advanced AI vision models with comprehensive understanding capabilities. Supports both local files and remote URL. Maximum file size: 5MB', {
82
+ image_source: z.string().describe('Local file path or remote URL to the image (supports PNG, JPG, JPEG)'),
83
+ prompt: z.string().describe('Detailed text prompt. If the task is **front-end code replication**, the prompt you provide must be: "Describe in detail the layout structure, color style, main components, and interactive elements of the website in this image to facilitate subsequent code generation by the model." + your additional requirements. \ For **other tasks**, the prompt you provide must clearly describe what to analyze, extract, or understand from the image.')
84
+ }, async (params) => {
85
+ try {
86
+ // Validate parameters
87
+ const validationSchema = new ToolSchemaBuilder()
88
+ .required('image_source', CommonSchemas.nonEmptyString)
89
+ .required('prompt', CommonSchemas.nonEmptyString)
90
+ .build();
91
+ validationSchema.parse(params);
92
+ // Build request object
93
+ const request = {
94
+ imageSource: params.image_source,
95
+ prompt: params.prompt
96
+ };
97
+ // Execute analysis
98
+ const result = await retryableAnalyze(request);
99
+ const response = createSuccessResponse(result);
100
+ return formatMcpResponse(response);
101
+ }
102
+ catch (error) {
103
+ console.error('Tool execution failed', {
104
+ error: error instanceof Error ? error.message : String(error),
105
+ params
106
+ });
107
+ let errorResponse;
108
+ if (error instanceof z.ZodError) {
109
+ const validationErrors = error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
110
+ errorResponse = createErrorResponse(`Validation failed: ${validationErrors}`);
111
+ }
112
+ else if (error instanceof FileNotFoundError) {
113
+ errorResponse = createErrorResponse(`Image file not found: ${error.message}`);
114
+ }
115
+ else if (error instanceof ApiError) {
116
+ errorResponse = createErrorResponse(`API error: ${error.message}`);
117
+ }
118
+ else {
119
+ errorResponse = createErrorResponse(`Unexpected error: ${error instanceof Error ? error.message : String(error)}`);
120
+ }
121
+ return formatMcpResponse(errorResponse);
122
+ }
123
+ });
124
+ console.info('Image analysis tool registered successfully');
125
+ }
@@ -0,0 +1,125 @@
1
+ import { z } from 'zod';
2
+ import { FileNotFoundError, ApiError } from '../types/index.js';
3
+ import { ToolExecutionError } from '../core/error-handler.js';
4
+ import { CommonSchemas, ToolSchemaBuilder } from '../utils/validation.js';
5
+ import { createMultiModalMessage, createVideoContent, formatMcpResponse, createSuccessResponse, createErrorResponse, withRetry } from '../core/api-common.js';
6
+ import { fileService } from '../core/file-service.js';
7
+ import { chatService } from '../core/chat-service.js';
8
+ /**
9
+ * Video analysis service class
10
+ */
11
+ export class VideoAnalysisService {
12
+ chatService = chatService;
13
+ fileService = fileService;
14
+ MAX_VIDEO_SIZE_MB = 8;
15
+ /**
16
+ * Execute video analysis
17
+ * @param request Video analysis request
18
+ * @returns Analysis result
19
+ */
20
+ async analyzeVideo(request) {
21
+ console.info('Starting video analysis', {
22
+ videoSource: request.videoSource,
23
+ prompt: request.prompt
24
+ });
25
+ try {
26
+ // Validate video source (file or URL) and size
27
+ await this.fileService.validateVideoSource(request.videoSource, this.MAX_VIDEO_SIZE_MB);
28
+ // Validate prompt
29
+ if (!request.prompt || request.prompt.trim().length === 0) {
30
+ throw new ToolExecutionError('Prompt is required for video analysis', 'video-analysis', 'VALIDATION_ERROR', {
31
+ toolName: 'video-analysis',
32
+ operation: 'analyzeVideo',
33
+ metadata: { videoSource: request.videoSource }
34
+ });
35
+ }
36
+ // Handle video source (URL or local file)
37
+ let videoContent;
38
+ if (this.fileService.isUrl(request.videoSource)) {
39
+ // For URLs, pass directly without base64 encoding
40
+ videoContent = createVideoContent(request.videoSource);
41
+ }
42
+ else {
43
+ // For local files, encode to base64
44
+ const videoData = await this.fileService.encodeVideoToBase64(request.videoSource);
45
+ videoContent = createVideoContent(videoData);
46
+ }
47
+ // Create multimodal message
48
+ const messages = createMultiModalMessage([videoContent], request.prompt);
49
+ const result = await this.chatService.visionCompletions(messages);
50
+ console.info('Video analysis completed', {
51
+ videoSource: request.videoSource
52
+ });
53
+ return result;
54
+ }
55
+ catch (error) {
56
+ console.error('Video analysis failed', {
57
+ error: error instanceof Error ? error.message : String(error),
58
+ videoSource: request.videoSource
59
+ });
60
+ if (error instanceof ToolExecutionError) {
61
+ throw error;
62
+ }
63
+ // Wrap unknown errors
64
+ throw new ToolExecutionError(`Video analysis failed: ${error.message}`, 'video-analysis', 'EXECUTION_ERROR', {
65
+ toolName: 'video-analysis',
66
+ operation: 'analyzeVideo',
67
+ metadata: { videoSource: request.videoSource, originalError: error }
68
+ }, error);
69
+ }
70
+ }
71
+ }
72
+ /**
73
+ * Register video analysis tool with MCP server
74
+ * @param server MCP server instance
75
+ */
76
+ export function registerVideoAnalysisTool(server) {
77
+ const analysisService = new VideoAnalysisService();
78
+ const retryableAnalyze = withRetry(analysisService.analyzeVideo.bind(analysisService), 2, // Maximum 2 retries
79
+ 1000 // 1 second delay
80
+ );
81
+ server.tool('analyze_video', 'Analyze a video using advanced AI vision models with comprehensive understanding capabilities. Supports both local files and remote URL. Maximum local file size: 8MB', {
82
+ video_source: z.string().describe('Local file path or remote URL to the video (supports MP4, MOV, M4V)'),
83
+ prompt: z.string().describe('Detailed text prompt describing what to analyze, extract, or understand from the video')
84
+ }, async (params) => {
85
+ try {
86
+ // Validate parameters
87
+ const validationSchema = new ToolSchemaBuilder()
88
+ .required('video_source', CommonSchemas.nonEmptyString)
89
+ .required('prompt', CommonSchemas.nonEmptyString)
90
+ .build();
91
+ validationSchema.parse(params);
92
+ // Build request object
93
+ const request = {
94
+ videoSource: params.video_source,
95
+ prompt: params.prompt
96
+ };
97
+ // Execute analysis
98
+ const result = await retryableAnalyze(request);
99
+ const response = createSuccessResponse(result);
100
+ return formatMcpResponse(response);
101
+ }
102
+ catch (error) {
103
+ console.error('Tool execution failed', {
104
+ error: error instanceof Error ? error.message : String(error),
105
+ params
106
+ });
107
+ let errorResponse;
108
+ if (error instanceof z.ZodError) {
109
+ const validationErrors = error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
110
+ errorResponse = createErrorResponse(`Validation failed: ${validationErrors}`);
111
+ }
112
+ else if (error instanceof FileNotFoundError) {
113
+ errorResponse = createErrorResponse(`Video file not found: ${error.message}`);
114
+ }
115
+ else if (error instanceof ApiError) {
116
+ errorResponse = createErrorResponse(`API error: ${error.message}`);
117
+ }
118
+ else {
119
+ errorResponse = createErrorResponse(`Unexpected error: ${error instanceof Error ? error.message : String(error)}`);
120
+ }
121
+ return formatMcpResponse(errorResponse);
122
+ }
123
+ });
124
+ console.info('Video analysis tool registered successfully');
125
+ }
@@ -0,0 +1,35 @@
1
+ // Error types
2
+ export class McpError extends Error {
3
+ code;
4
+ context;
5
+ constructor(message, code, context) {
6
+ super(message);
7
+ this.code = code;
8
+ this.context = context;
9
+ this.name = 'McpError';
10
+ }
11
+ }
12
+ export class ValidationError extends McpError {
13
+ field;
14
+ constructor(message, context, field) {
15
+ super(message, 'VALIDATION_ERROR', context);
16
+ this.field = field;
17
+ this.name = 'ValidationError';
18
+ }
19
+ }
20
+ export class ApiError extends McpError {
21
+ statusCode;
22
+ details;
23
+ constructor(message, context, statusCode, details) {
24
+ super(message, 'API_ERROR', context);
25
+ this.statusCode = statusCode;
26
+ this.details = details;
27
+ this.name = 'ApiError';
28
+ }
29
+ }
30
+ export class FileNotFoundError extends McpError {
31
+ constructor(filePath) {
32
+ super(`File not found: ${filePath}`, 'FILE_NOT_FOUND', { filePath });
33
+ this.name = 'FileNotFoundError';
34
+ }
35
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Logger utility that writes to stderr and a log file.
3
+ * Stderr keeps MCP JSON on stdout clean; file provides persistent logs.
4
+ */
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import * as os from 'os';
8
+ class Logger {
9
+ logStream;
10
+ logFilePath;
11
+ constructor(logFilePath) {
12
+ if (logFilePath) {
13
+ this.setLogFile(logFilePath);
14
+ }
15
+ }
16
+ setLogFile(logFilePath) {
17
+ try {
18
+ this.logFilePath = logFilePath;
19
+ const dir = path.dirname(logFilePath);
20
+ fs.mkdirSync(dir, { recursive: true });
21
+ if (this.logStream) {
22
+ this.logStream.end();
23
+ }
24
+ this.logStream = fs.createWriteStream(logFilePath, { flags: 'a' });
25
+ }
26
+ catch (err) {
27
+ const timestamp = new Date().toISOString();
28
+ const msg = `[${timestamp}] ERROR: Failed to initialize log file '${logFilePath}': ${String(err)}`;
29
+ process.stderr.write(msg + '\n');
30
+ }
31
+ }
32
+ safeStringify(obj) {
33
+ // Serialize Error objects so that message/stack and custom fields are visible
34
+ const replacer = (_key, value) => {
35
+ if (value instanceof Error) {
36
+ const base = {
37
+ name: value.name,
38
+ message: value.message,
39
+ stack: value.stack
40
+ };
41
+ // Include enumerable own properties (e.g., code, statusCode, details, context)
42
+ for (const k of Object.keys(value)) {
43
+ if (!(k in base)) {
44
+ base[k] = value[k];
45
+ }
46
+ }
47
+ return base;
48
+ }
49
+ return value;
50
+ };
51
+ try {
52
+ return JSON.stringify(obj, replacer);
53
+ }
54
+ catch {
55
+ try {
56
+ return String(obj);
57
+ }
58
+ catch {
59
+ return '[Unserializable]';
60
+ }
61
+ }
62
+ }
63
+ write(level, message, ...args) {
64
+ const timestamp = new Date().toISOString();
65
+ const serializedArgs = args.length > 0 ? ` ${this.safeStringify(args)}` : '';
66
+ const logMessage = `[${timestamp}] ${level.toUpperCase()}: ${message}${serializedArgs}`;
67
+ // Write to stderr (visible console output without polluting stdout)
68
+ process.stderr.write(logMessage + '\n');
69
+ // Write to log file if configured
70
+ if (this.logStream) {
71
+ this.logStream.write(logMessage + '\n');
72
+ }
73
+ }
74
+ info(message, ...args) {
75
+ this.write('info', message, ...args);
76
+ }
77
+ error(message, ...args) {
78
+ this.write('error', message, ...args);
79
+ }
80
+ warn(message, ...args) {
81
+ this.write('warn', message, ...args);
82
+ }
83
+ debug(message, ...args) {
84
+ this.write('debug', message, ...args);
85
+ }
86
+ log(message, ...args) {
87
+ this.write('info', message, ...args);
88
+ }
89
+ }
90
+ export const logger = new Logger();
91
+ /**
92
+ * Override global console to redirect to stderr
93
+ * This prevents console output from interfering with MCP JSON protocol
94
+ */
95
+ export function setupConsoleRedirection() {
96
+ const originalConsole = { ...console };
97
+ // Cross-platform log file path:
98
+ // - If env ZAI_MCP_LOG_PATH is set, use it
99
+ // - Otherwise use user home directory: ~/.zai/zai-mcp-YYYY-MM-DD.log (Windows/macOS/Linux)
100
+ const resolveLogFilePath = () => {
101
+ const envPath = process.env.ZAI_MCP_LOG_PATH;
102
+ if (envPath && envPath.trim().length > 0) {
103
+ return path.resolve(envPath);
104
+ }
105
+ const homeDir = os.homedir();
106
+ const now = new Date();
107
+ const yyyy = now.getFullYear();
108
+ const mm = String(now.getMonth() + 1).padStart(2, '0');
109
+ const dd = String(now.getDate()).padStart(2, '0');
110
+ const dateStr = `${yyyy}-${mm}-${dd}`;
111
+ return path.join(homeDir, '.zai', `zai-mcp-${dateStr}.log`);
112
+ };
113
+ logger.setLogFile(resolveLogFilePath());
114
+ console.info = logger.info.bind(logger);
115
+ console.error = logger.error.bind(logger);
116
+ console.warn = logger.warn.bind(logger);
117
+ console.debug = logger.debug.bind(logger);
118
+ console.log = logger.log.bind(logger);
119
+ return originalConsole;
120
+ }
@@ -0,0 +1,198 @@
1
+ import { z } from 'zod';
2
+ import { ValidationError } from '../types/index.js';
3
+ /**
4
+ * Runtime type validator
5
+ */
6
+ export class RuntimeValidator {
7
+ /**
8
+ * Validate data against specified Zod schema
9
+ */
10
+ static validate(data, schema, options = {}) {
11
+ const { throwOnError = true, customMessage, logErrors = true } = options;
12
+ try {
13
+ const result = schema.parse(data);
14
+ return {
15
+ success: true,
16
+ data: result
17
+ };
18
+ }
19
+ catch (error) {
20
+ const validationErrors = this.parseZodError(error);
21
+ if (logErrors) {
22
+ console.warn('Validation failed', {
23
+ errors: validationErrors,
24
+ data: this.sanitizeData(data)
25
+ });
26
+ }
27
+ if (throwOnError) {
28
+ const message = customMessage || `Validation failed: ${validationErrors.map(e => e.message).join(', ')}`;
29
+ throw new ValidationError(message, { errors: validationErrors });
30
+ }
31
+ return {
32
+ success: false,
33
+ error: {
34
+ message: validationErrors.map(e => e.message).join(', '),
35
+ code: 'VALIDATION_ERROR'
36
+ }
37
+ };
38
+ }
39
+ }
40
+ /**
41
+ * Safe validation, does not throw exceptions
42
+ */
43
+ static safeValidate(data, schema) {
44
+ return this.validate(data, schema, { throwOnError: false });
45
+ }
46
+ /**
47
+ * Parse Zod error to standard validation error format
48
+ */
49
+ static parseZodError(error) {
50
+ return error.issues.map(issue => ({
51
+ message: issue.message,
52
+ field: issue.path.join('.'),
53
+ code: issue.code,
54
+ expected: this.getExpectedType(issue),
55
+ received: 'received' in issue ? String(issue.received) : 'unknown'
56
+ }));
57
+ }
58
+ /**
59
+ * Get expected type description
60
+ */
61
+ static getExpectedType(issue) {
62
+ switch (issue.code) {
63
+ case 'invalid_type':
64
+ return issue.expected;
65
+ case 'too_small':
66
+ return `minimum ${issue.minimum}`;
67
+ case 'too_big':
68
+ return `maximum ${issue.maximum}`;
69
+ default:
70
+ return 'valid value';
71
+ }
72
+ }
73
+ /**
74
+ * Sanitize sensitive data for logging
75
+ */
76
+ static sanitizeData(data) {
77
+ if (typeof data !== 'object' || data === null) {
78
+ return data;
79
+ }
80
+ const sensitiveKeys = ['password', 'token', 'secret', 'key', 'auth'];
81
+ const sanitized = { ...data };
82
+ for (const key of Object.keys(sanitized)) {
83
+ if (sensitiveKeys.some(sensitive => key.toLowerCase().includes(sensitive))) {
84
+ sanitized[key] = '[REDACTED]';
85
+ }
86
+ }
87
+ return sanitized;
88
+ }
89
+ }
90
+ /**
91
+ * Parameter validation decorator
92
+ */
93
+ export function ValidateParams(schema, options = {}) {
94
+ return function (target, propertyKey, descriptor) {
95
+ const originalMethod = descriptor.value;
96
+ descriptor.value = async function (...args) {
97
+ // Assume the first parameter is the parameter object to validate
98
+ const params = args[0];
99
+ try {
100
+ const validationResult = RuntimeValidator.validate(params, schema, options);
101
+ if (!validationResult.success) {
102
+ throw new ValidationError('Parameter validation failed', { errors: validationResult.errors });
103
+ }
104
+ // Replace original parameters with validated data
105
+ args[0] = validationResult.data;
106
+ return await originalMethod.apply(this, args);
107
+ }
108
+ catch (error) {
109
+ console.error(`Parameter validation failed for ${propertyKey}`, {
110
+ error: error instanceof Error ? error.message : String(error),
111
+ params: RuntimeValidator['sanitizeData'](params)
112
+ });
113
+ throw error;
114
+ }
115
+ };
116
+ return descriptor;
117
+ };
118
+ }
119
+ /**
120
+ * Return value validation decorator
121
+ */
122
+ export function ValidateReturn(schema, options = {}) {
123
+ return function (target, propertyKey, descriptor) {
124
+ const originalMethod = descriptor.value;
125
+ descriptor.value = async function (...args) {
126
+ const result = await originalMethod.apply(this, args);
127
+ try {
128
+ const validationResult = RuntimeValidator.validate(result, schema, options);
129
+ if (!validationResult.success) {
130
+ throw new ValidationError('Return value validation failed', { errors: validationResult.errors });
131
+ }
132
+ return validationResult.data;
133
+ }
134
+ catch (error) {
135
+ console.error(`Return value validation failed for ${propertyKey}`, {
136
+ error: error instanceof Error ? error.message : String(error),
137
+ result: RuntimeValidator['sanitizeData'](result)
138
+ });
139
+ throw error;
140
+ }
141
+ };
142
+ return descriptor;
143
+ };
144
+ }
145
+ /**
146
+ * Common validation schemas
147
+ */
148
+ export const CommonSchemas = {
149
+ /** Non-empty string */
150
+ nonEmptyString: z.string().min(1, 'String cannot be empty'),
151
+ /** Positive integer */
152
+ positiveInteger: z.number().int().positive('Must be a positive integer'),
153
+ /** Non-negative integer */
154
+ nonNegativeInteger: z.number().int().min(0, 'Must be a non-negative integer'),
155
+ /** URL format */
156
+ url: z.string().url('Must be a valid URL'),
157
+ /** Email format */
158
+ email: z.string().email('Must be a valid email address'),
159
+ /** UUID format */
160
+ uuid: z.string().uuid('Must be a valid UUID'),
161
+ /** File path */
162
+ filePath: z.string().min(1).refine((path) => !path.includes('..'), 'File path cannot contain ".."'),
163
+ /** Tool name */
164
+ toolName: z.string().regex(/^[a-z][a-z0-9-]*[a-z0-9]$/, 'Tool name must be lowercase, start with letter, and contain only letters, numbers, and hyphens')
165
+ };
166
+ /**
167
+ * Tool parameter validation schema builder
168
+ */
169
+ export class ToolSchemaBuilder {
170
+ schema = {};
171
+ /**
172
+ * Add required field
173
+ */
174
+ required(name, schema) {
175
+ this.schema[name] = schema;
176
+ return this;
177
+ }
178
+ /**
179
+ * Add optional field
180
+ */
181
+ optional(name, schema) {
182
+ this.schema[name] = schema.optional();
183
+ return this;
184
+ }
185
+ /**
186
+ * Add field with default value
187
+ */
188
+ withDefault(name, schema, defaultValue) {
189
+ this.schema[name] = schema.default(() => defaultValue);
190
+ return this;
191
+ }
192
+ /**
193
+ * Build final validation schema
194
+ */
195
+ build() {
196
+ return z.object(this.schema);
197
+ }
198
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@z_ai/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP Server for Z.AI - A Model Context Protocol server that provides AI capabilities",
5
+ "main": "build/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "zai-mcp-server": "./build/index.js"
9
+ },
10
+ "homepage": "https://docs.z.ai/",
11
+ "bugs": {
12
+ "url": "https://docs.z.ai/"
13
+ },
14
+ "scripts": {
15
+ "build": "tsc && chmod 755 build/index.js",
16
+ "start": "node build/index.js",
17
+ "prepare": "npm run build",
18
+ "prerelease": "npm version prerelease --preid=beta",
19
+ "publish-beta": "npm publish --tag beta",
20
+ "publish": "npm publish"
21
+ },
22
+ "keywords": [
23
+ "zai",
24
+ "mcp",
25
+ "vision"
26
+ ],
27
+ "author": "Z.AI",
28
+ "contributors": [
29
+ "Chao Gong (https://github.com/tomsun28)",
30
+ "Lei Yuan (https://github.com/Web-Life)"
31
+ ],
32
+ "license": "Apache-2.0",
33
+ "files": [
34
+ "build",
35
+ "README.zh-CN.md",
36
+ "README.md"
37
+ ],
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "dependencies": {
45
+ "zod": "^3.23.8",
46
+ "@modelcontextprotocol/sdk": "1.17.5"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "24.3.1",
50
+ "ts-node": "10.9.2",
51
+ "typescript": "5.9.2"
52
+ }
53
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "luma-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "A vision understanding MCP server powered by GLM-4.5V",
5
5
  "type": "module",
6
6
  "bin": {
7
- "luma-mcp": "./build/index.js"
7
+ "luma-mcp": "build/index.js"
8
8
  },
9
9
  "main": "./build/index.js",
10
10
  "scripts": {
@@ -25,7 +25,7 @@
25
25
  "license": "MIT",
26
26
  "repository": {
27
27
  "type": "git",
28
- "url": "https://github.com/JochenYang/luma-mcp.git"
28
+ "url": "git+https://github.com/JochenYang/luma-mcp.git"
29
29
  },
30
30
  "bugs": {
31
31
  "url": "https://github.com/JochenYang/luma-mcp/issues"