luma-mcp 1.0.2 → 1.1.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.
Files changed (40) hide show
  1. package/.env.example +39 -0
  2. package/CHANGELOG.md +50 -0
  3. package/README.md +187 -32
  4. package/build/config.d.ts +2 -0
  5. package/build/config.d.ts.map +1 -1
  6. package/build/config.js +24 -8
  7. package/build/config.js.map +1 -1
  8. package/build/index.js +17 -6
  9. package/build/index.js.map +1 -1
  10. package/build/siliconflow-client.d.ts +23 -0
  11. package/build/siliconflow-client.d.ts.map +1 -0
  12. package/build/siliconflow-client.js +85 -0
  13. package/build/siliconflow-client.js.map +1 -0
  14. package/build/vision-client.d.ts +18 -0
  15. package/build/vision-client.d.ts.map +1 -0
  16. package/build/vision-client.js +5 -0
  17. package/build/vision-client.js.map +1 -0
  18. package/build/zhipu-client.d.ts +6 -1
  19. package/build/zhipu-client.d.ts.map +1 -1
  20. package/build/zhipu-client.js +10 -3
  21. package/build/zhipu-client.js.map +1 -1
  22. package/package.json +8 -3
  23. package/test/test-deepseek-raw.ts +94 -0
  24. package/test/test-local.ts +20 -7
  25. package/.claude/settings.local.json +0 -10
  26. package/mcp-server/README.md +0 -41
  27. package/mcp-server/README.zh-CN.md +0 -42
  28. package/mcp-server/build/core/api-common.js +0 -122
  29. package/mcp-server/build/core/chat-service.js +0 -80
  30. package/mcp-server/build/core/environment.js +0 -128
  31. package/mcp-server/build/core/error-handler.js +0 -376
  32. package/mcp-server/build/core/file-service.js +0 -126
  33. package/mcp-server/build/index.js +0 -160
  34. package/mcp-server/build/tools/image-analysis.js +0 -125
  35. package/mcp-server/build/tools/video-analysis.js +0 -125
  36. package/mcp-server/build/types/index.js +0 -35
  37. package/mcp-server/build/types/validation-types.js +0 -1
  38. package/mcp-server/build/utils/logger.js +0 -120
  39. package/mcp-server/build/utils/validation.js +0 -198
  40. package/mcp-server/package.json +0 -53
@@ -1,160 +0,0 @@
1
- #!/usr/bin/env node
2
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
- import { configurationService } from './core/environment.js';
5
- import { handleError } from './core/error-handler.js';
6
- import { setupConsoleRedirection } from './utils/logger.js';
7
- // Setup console redirection BEFORE any other code to prevent stdout pollution
8
- setupConsoleRedirection();
9
- // Import tool registration functions
10
- import { registerImageAnalysisTool } from './tools/image-analysis.js';
11
- import { registerVideoAnalysisTool } from './tools/video-analysis.js';
12
- import { McpError } from "./types/index.js";
13
- /**
14
- * MCP Server Application class
15
- */
16
- class McpServerApplication {
17
- server;
18
- constructor() {
19
- this.server = new McpServer({
20
- name: configurationService.getServerConfig().name,
21
- version: configurationService.getServerConfig().version
22
- }, {
23
- capabilities: {
24
- tools: {}
25
- }
26
- });
27
- this.setupErrorHandling();
28
- console.info('MCP Server Application initialized');
29
- }
30
- /**
31
- * Register all tools
32
- */
33
- async registerTools() {
34
- try {
35
- // Register tools directly with server
36
- registerImageAnalysisTool(this.server);
37
- registerVideoAnalysisTool(this.server);
38
- console.info('Successfully registered all tools');
39
- }
40
- catch (error) {
41
- const standardError = await handleError(error, {
42
- operation: 'tool-registration',
43
- metadata: { component: 'McpServerApplication' }
44
- });
45
- console.error('Failed to register tools', standardError);
46
- throw standardError;
47
- }
48
- }
49
- /**
50
- * Setup error handling
51
- */
52
- setupErrorHandling() {
53
- process.on('uncaughtException', (error) => {
54
- console.error('Uncaught exception:', error);
55
- this.gracefulShutdown(1);
56
- });
57
- process.on('unhandledRejection', (reason, promise) => {
58
- console.error('Unhandled rejection at:', { promise, reason });
59
- this.gracefulShutdown(1);
60
- });
61
- process.on('SIGINT', () => {
62
- console.info('Received SIGINT, shutting down gracefully...');
63
- this.gracefulShutdown(0);
64
- });
65
- process.on('SIGTERM', () => {
66
- console.info('Received SIGTERM, shutting down gracefully...');
67
- this.gracefulShutdown(0);
68
- });
69
- }
70
- /**
71
- * Graceful shutdown
72
- */
73
- gracefulShutdown(exitCode) {
74
- try {
75
- console.info('Performing graceful shutdown...');
76
- // Cleanup logic can be added here
77
- process.exit(exitCode);
78
- }
79
- catch (error) {
80
- console.error('Error during graceful shutdown:', { error });
81
- process.exit(1);
82
- }
83
- }
84
- /**
85
- * Start server
86
- */
87
- async start() {
88
- try {
89
- console.info('Starting MCP server...');
90
- // Set up global error handling
91
- process.on('uncaughtException', async (error) => {
92
- const standardError = await handleError(error, {
93
- operation: 'uncaughtException',
94
- metadata: { source: 'process' }
95
- });
96
- console.error('Uncaught exception:', standardError);
97
- process.exit(1);
98
- });
99
- process.on('unhandledRejection', async (reason) => {
100
- const error = reason instanceof Error ? reason : new Error(String(reason));
101
- const standardError = await handleError(error, {
102
- operation: 'unhandledRejection',
103
- metadata: { source: 'process' }
104
- });
105
- console.error('Unhandled Promise rejection:', standardError);
106
- process.exit(1);
107
- });
108
- // Register tools
109
- await this.registerTools();
110
- // Create transport layer
111
- const transport = new StdioServerTransport();
112
- // Start server
113
- await this.server.connect(transport);
114
- console.info('MCP Server started successfully', {
115
- mode: configurationService.getPlatformModel(),
116
- name: configurationService.getServerConfig().name,
117
- version: configurationService.getServerConfig().version
118
- });
119
- }
120
- catch (error) {
121
- const standardError = await handleError(error, {
122
- operation: 'server-start',
123
- metadata: { component: 'McpServerApplication' }
124
- });
125
- console.error('Server startup failed:', standardError);
126
- throw standardError;
127
- }
128
- }
129
- /**
130
- * Gracefully shutdown server
131
- */
132
- async shutdown() {
133
- try {
134
- console.info('Shutting down MCP server...');
135
- console.info('MCP server shutdown completed');
136
- }
137
- catch (error) {
138
- console.error('Error during server shutdown', { error });
139
- throw error;
140
- }
141
- }
142
- }
143
- // Start application
144
- async function main() {
145
- try {
146
- const app = new McpServerApplication();
147
- await app.start();
148
- }
149
- catch (error) {
150
- if (error instanceof McpError) {
151
- console.error('Application startup failed:', { message: error.message });
152
- }
153
- else {
154
- console.error('Application startup failed:', { error });
155
- }
156
- process.exit(1);
157
- }
158
- }
159
- // Start main program
160
- main();
@@ -1,125 +0,0 @@
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
- }
@@ -1,125 +0,0 @@
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
- }
@@ -1,35 +0,0 @@
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
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,120 +0,0 @@
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
- }