luma-mcp 1.2.1 → 1.2.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 (38) hide show
  1. package/.github/workflows/release.yml +0 -17
  2. package/CHANGELOG.md +21 -0
  3. package/README.md +5 -1
  4. package/build/image-processor.d.ts.map +1 -1
  5. package/build/image-processor.js +33 -17
  6. package/build/image-processor.js.map +1 -1
  7. package/build/prompts.d.ts +1 -1
  8. package/build/prompts.d.ts.map +1 -1
  9. package/build/prompts.js +14 -28
  10. package/build/prompts.js.map +1 -1
  11. package/minimax_coding_plan_mcp-0.0.2/.env.test +4 -0
  12. package/minimax_coding_plan_mcp-0.0.2/LICENSE +21 -0
  13. package/minimax_coding_plan_mcp-0.0.2/PKG-INFO +200 -0
  14. package/minimax_coding_plan_mcp-0.0.2/README.md +143 -0
  15. package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/PKG-INFO +200 -0
  16. package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/SOURCES.txt +17 -0
  17. package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/dependency_links.txt +1 -0
  18. package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/entry_points.txt +2 -0
  19. package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/requires.txt +20 -0
  20. package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/top_level.txt +1 -0
  21. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__init__.py +3 -0
  22. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__main__.py +99 -0
  23. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  24. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__pycache__/client.cpython-313.pyc +0 -0
  25. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__pycache__/const.cpython-313.pyc +0 -0
  26. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__pycache__/exceptions.cpython-313.pyc +0 -0
  27. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__pycache__/utils.cpython-313.pyc +0 -0
  28. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/client.py +104 -0
  29. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/const.py +4 -0
  30. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/exceptions.py +24 -0
  31. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/server.py +169 -0
  32. package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/utils.py +101 -0
  33. package/minimax_coding_plan_mcp-0.0.2/pyproject.toml +59 -0
  34. package/minimax_coding_plan_mcp-0.0.2/setup.cfg +4 -0
  35. package/minimax_coding_plan_mcp-0.0.2/setup.py +6 -0
  36. package/minimax_coding_plan_mcp-0.0.2/test_at_prefix.py +134 -0
  37. package/minimax_coding_plan_mcp-0.0.2/test_real_image.py +153 -0
  38. package/package.json +1 -1
@@ -0,0 +1,169 @@
1
+ """
2
+ MiniMax Coding Plan MCP Server
3
+
4
+ ⚠️ IMPORTANT: This server connects to Minimax API endpoints which may involve costs.
5
+ Any tool that makes an API call is clearly marked with a cost warning. Please follow these guidelines:
6
+
7
+ 1. Only use these tools when users specifically ask for them
8
+ 2. For audio generation tools, be mindful that text length affects the cost
9
+ 3. Voice cloning features are charged upon first use after cloning
10
+
11
+ Note: Tools without cost warnings are free to use as they only read existing data.
12
+ """
13
+
14
+ import os
15
+ import json
16
+ from dotenv import load_dotenv
17
+ from mcp.server.fastmcp import FastMCP
18
+ from mcp.types import TextContent
19
+ from minimax_mcp.utils import (
20
+ process_image_url,
21
+ )
22
+
23
+ from minimax_mcp.const import *
24
+ from minimax_mcp.exceptions import MinimaxAPIError, MinimaxRequestError
25
+ from minimax_mcp.client import MinimaxAPIClient
26
+
27
+ load_dotenv()
28
+ api_key = os.getenv(ENV_MINIMAX_API_KEY)
29
+ api_host = os.getenv(ENV_MINIMAX_API_HOST)
30
+ fastmcp_log_level = os.getenv(ENV_FASTMCP_LOG_LEVEL) or "WARNING"
31
+
32
+ if not api_key:
33
+ raise ValueError("MINIMAX_API_KEY environment variable is required")
34
+ if not api_host:
35
+ raise ValueError("MINIMAX_API_HOST environment variable is required")
36
+
37
+ mcp = FastMCP("Minimax",log_level=fastmcp_log_level)
38
+ api_client = MinimaxAPIClient(api_key, api_host)
39
+
40
+ @mcp.tool(
41
+ description="""
42
+
43
+ Web Search API, works like Google Search.
44
+
45
+ Args:
46
+ query (str): The search query string. Use 3-5 keywords. Add the current date for time-sensitive queries, e.g. `latest iPhone 2025`
47
+
48
+ Search Strategy:
49
+ - If no valid results found, try changing search keywords
50
+
51
+ Returns:
52
+ Text content with search results in JSON format. The response structure is:
53
+ {
54
+ "organic": [
55
+ {
56
+ "title": "string - The title of the search result",
57
+ "link": "string - The URL link to the result",
58
+ "snippet": "string - A brief description or excerpt",
59
+ "date": "string - The date of the result"
60
+ }
61
+ ],
62
+ "related_searches": [
63
+ {
64
+ "query": "string - A related search query suggestion"
65
+ }
66
+ ],
67
+ "base_resp": {
68
+ "status_code": "int - Response status code",
69
+ "status_msg": "string - Response status message"
70
+ }
71
+ }
72
+ """
73
+ )
74
+ def web_search(
75
+ query: str,
76
+ ) -> TextContent:
77
+ try:
78
+ if not query:
79
+ raise MinimaxRequestError("Query is required")
80
+
81
+ # Build request payload
82
+ payload = {
83
+ "q": query
84
+ }
85
+
86
+ # Call search API
87
+ response_data = api_client.post("/v1/coding_plan/search", json=payload)
88
+
89
+ # Return JSON dump of response data
90
+ return TextContent(
91
+ type="text",
92
+ text=json.dumps(response_data, ensure_ascii=False, indent=2)
93
+ )
94
+
95
+ except MinimaxAPIError as e:
96
+ return TextContent(
97
+ type="text",
98
+ text=f"Failed to perform search: {str(e)}"
99
+ )
100
+
101
+
102
+ @mcp.tool(
103
+ description="""
104
+
105
+ A powerful LLM that can analyze and understand image content from files or URLs, follow your instruction.
106
+ Use this tool to understand images by LLM.
107
+ Only support jpeg, png, webp formats. Other formats like pdf/gif/psd/svg and so on are not supported.
108
+
109
+ Args:
110
+ prompt (str): The text prompt describing what you want to analyze or extract from the image.
111
+ image_source (str): The source location of the image to analyze.
112
+ Accepts:
113
+ - HTTP/HTTPS URL: "https://example.com/image.jpg"
114
+ - Local file path: "/path/to/image.png"
115
+ Supported formats: JPEG, PNG, WebP
116
+
117
+ Returns:
118
+ Text content with the image analysis result.
119
+ """
120
+ )
121
+ def understand_image(
122
+ prompt: str,
123
+ image_source: str,
124
+ ) -> TextContent:
125
+ try:
126
+ if not prompt:
127
+ raise MinimaxRequestError("Prompt is required")
128
+ if not image_source:
129
+ raise MinimaxRequestError("Image source is required")
130
+
131
+ # Process image_source: convert HTTP URL or local file to base64, or pass through existing base64
132
+ processed_image_url = process_image_url(image_source)
133
+
134
+ # Build request payload
135
+ payload = {
136
+ "prompt": prompt,
137
+ "image_url": processed_image_url
138
+ }
139
+
140
+ # Call VLM API
141
+ response_data = api_client.post("/v1/coding_plan/vlm", json=payload)
142
+
143
+ # Extract content from response
144
+ content = response_data.get("content", "")
145
+
146
+ if not content:
147
+ raise MinimaxRequestError("No content returned from VLM API")
148
+
149
+ # Return the content
150
+ return TextContent(
151
+ type="text",
152
+ text=content
153
+ )
154
+
155
+ except MinimaxAPIError as e:
156
+ return TextContent(
157
+ type="text",
158
+ text=f"Failed to perform VLM analysis: {str(e)}"
159
+ )
160
+
161
+
162
+ def main():
163
+ print("Starting Minimax MCP server")
164
+ """Run the Minimax MCP server"""
165
+ mcp.run()
166
+
167
+
168
+ if __name__ == "__main__":
169
+ main()
@@ -0,0 +1,101 @@
1
+ import os
2
+ import base64
3
+ import requests
4
+ from minimax_mcp.const import *
5
+ from minimax_mcp.exceptions import MinimaxMcpError, MinimaxRequestError
6
+
7
+
8
+ def strip_at_prefix(path: str) -> str:
9
+ """
10
+ Remove @ prefix from Claude Code file references.
11
+
12
+ Claude Code uses @ as a syntax sugar for workspace files.
13
+ For example: @test-minimax-image/image.png -> test-minimax-image/image.png
14
+
15
+ Args:
16
+ path (str): The file path that may have @ prefix
17
+
18
+ Returns:
19
+ str: Path without @ prefix
20
+ """
21
+ if path.startswith('@'):
22
+ return path[1:]
23
+ return path
24
+
25
+
26
+ def process_image_url(image_url: str) -> str:
27
+ """
28
+ Process image URL and convert to base64 data URL format.
29
+
30
+ This function handles three types of image inputs:
31
+ 1. HTTP/HTTPS URLs: Downloads the image and converts to base64
32
+ 2. Base64 data URLs: Passes through as-is
33
+ 3. Local file paths: Reads the file and converts to base64
34
+
35
+ Args:
36
+ image_url (str): The image URL, data URL, or local file path
37
+
38
+ Returns:
39
+ str: Base64 data URL in format "data:image/{format};base64,{data}"
40
+
41
+ Raises:
42
+ MinimaxRequestError: If image cannot be downloaded, read, or processed
43
+ """
44
+ # Remove @ prefix if present (Claude Code syntax)
45
+ image_url = strip_at_prefix(image_url)
46
+
47
+ # If already in base64 data URL format, pass through
48
+ if image_url.startswith("data:"):
49
+ return image_url
50
+
51
+ # Handle HTTP/HTTPS URLs
52
+ if image_url.startswith(("http://", "https://")):
53
+ try:
54
+ image_response = requests.get(image_url)
55
+ image_response.raise_for_status()
56
+ image_data = image_response.content
57
+
58
+ # Detect image format from content-type header
59
+ content_type = image_response.headers.get('content-type', '').lower()
60
+ if 'jpeg' in content_type or 'jpg' in content_type:
61
+ image_format = 'jpeg'
62
+ elif 'png' in content_type:
63
+ image_format = 'png'
64
+ elif 'webp' in content_type:
65
+ image_format = 'webp'
66
+ else:
67
+ # Default to jpeg if cannot detect
68
+ image_format = 'jpeg'
69
+
70
+ # Convert to base64 data URL
71
+ base64_data = base64.b64encode(image_data).decode('utf-8')
72
+ return f"data:image/{image_format};base64,{base64_data}"
73
+
74
+ except requests.RequestException as e:
75
+ raise MinimaxRequestError(f"Failed to download image from URL: {str(e)}")
76
+
77
+ # Handle local file paths
78
+ else:
79
+ if not os.path.exists(image_url):
80
+ raise MinimaxRequestError(f"Local image file does not exist: {image_url}")
81
+
82
+ try:
83
+ with open(image_url, "rb") as f:
84
+ image_data = f.read()
85
+
86
+ # Detect image format from file extension
87
+ image_format = 'jpeg' # Default
88
+ if image_url.lower().endswith('.png'):
89
+ image_format = 'png'
90
+ elif image_url.lower().endswith('.webp'):
91
+ image_format = 'webp'
92
+ elif image_url.lower().endswith(('.jpg', '.jpeg')):
93
+ image_format = 'jpeg'
94
+
95
+ base64_data = base64.b64encode(image_data).decode('utf-8')
96
+ return f"data:image/{image_format};base64,{base64_data}"
97
+
98
+ except IOError as e:
99
+ raise MinimaxRequestError(f"Failed to read local image file: {str(e)}")
100
+
101
+
@@ -0,0 +1,59 @@
1
+ [project]
2
+ name = "minimax-coding-plan-mcp"
3
+ version = "0.0.2"
4
+ description = "Specialized MiniMax Model Context Protocol (MCP) server designed for coding-plan users"
5
+ authors = [
6
+ { name = "Roy Wu", email = "zhengyu@minimax.chat" },
7
+ ]
8
+ readme = "README.md"
9
+ license = { file = "LICENSE" }
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.10",
16
+ ]
17
+ keywords = [
18
+ "minimax",
19
+ "mcp",
20
+ "web_search",
21
+ "understand-image",
22
+ ]
23
+ requires-python = ">=3.10"
24
+ dependencies = [
25
+ "mcp[cli]>=1.6.0",
26
+ "fastapi>=0.109.2",
27
+ "uvicorn>=0.27.1",
28
+ "python-dotenv>=1.0.1",
29
+ "pydantic>=2.6.1",
30
+ "httpx>=0.28.1",
31
+ "fuzzywuzzy>=0.18.0",
32
+ "python-Levenshtein>=0.25.0",
33
+ "sounddevice>=0.5.1",
34
+ "soundfile>=0.13.1",
35
+ "requests>=2.31.0",
36
+ ]
37
+
38
+ [project.scripts]
39
+ minimax-coding-plan-mcp = "minimax_mcp.server:main"
40
+
41
+ [project.optional-dependencies]
42
+ dev = [
43
+ "pre-commit>=3.6.2",
44
+ "ruff>=0.3.0",
45
+ "fastmcp>=0.4.1",
46
+ "pytest>=8.0.0",
47
+ "pytest-cov>=4.1.0",
48
+ "twine>=6.1.0",
49
+ "build>=1.0.3",
50
+ ]
51
+
52
+ [build-system]
53
+ requires = ["setuptools>=45", "wheel"]
54
+ build-backend = "setuptools.build_meta"
55
+
56
+ [tool.pytest.ini_options]
57
+ testpaths = ["tests"]
58
+ python_files = ["test_*.py"]
59
+ addopts = "-v --cov=minimax_mcp --cov-report=term-missing"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ packages=find_packages(),
5
+ include_package_data=True,
6
+ )
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test script to verify @ prefix handling
4
+ """
5
+
6
+ import sys
7
+ import os
8
+
9
+ # Add parent directory to path
10
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
11
+
12
+ from minimax_mcp.utils import strip_at_prefix, process_image_url
13
+ from minimax_mcp.exceptions import MinimaxRequestError
14
+
15
+
16
+ def test_strip_at_prefix():
17
+ """Test @ prefix removal"""
18
+ print("Testing strip_at_prefix()...")
19
+
20
+ # Test cases
21
+ test_cases = [
22
+ ("@test-minimax-image/image.png", "test-minimax-image/image.png"),
23
+ ("@folder/subfolder/image.jpg", "folder/subfolder/image.jpg"),
24
+ ("./normal/path.png", "./normal/path.png"),
25
+ ("D:\\images\\test.png", "D:\\images\\test.png"),
26
+ ("https://example.com/image.jpg", "https://example.com/image.jpg"),
27
+ ("data:image/png;base64,abc123", "data:image/png;base64,abc123"),
28
+ ]
29
+
30
+ all_passed = True
31
+ for input_path, expected in test_cases:
32
+ result = strip_at_prefix(input_path)
33
+ passed = result == expected
34
+ status = "✅ PASS" if passed else "❌ FAIL"
35
+ print(f"{status}: '{input_path}' -> '{result}' (expected: '{expected}')")
36
+ if not passed:
37
+ all_passed = False
38
+
39
+ return all_passed
40
+
41
+
42
+ def test_process_image_url_with_at_prefix():
43
+ """Test process_image_url with @ prefix"""
44
+ print("\nTesting process_image_url() with @ prefix...")
45
+
46
+ # Create a test image file
47
+ test_image_path = "test_image_for_at_prefix.png"
48
+
49
+ # Create a simple 1x1 PNG
50
+ import base64
51
+ tiny_png_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
52
+ with open(test_image_path, "wb") as f:
53
+ f.write(base64.b64decode(tiny_png_base64))
54
+
55
+ try:
56
+ # Test with @ prefix
57
+ result = process_image_url(f"@{test_image_path}")
58
+ print(f"✅ PASS: Successfully processed '@{test_image_path}'")
59
+ print(f" Result starts with: {result[:50]}...")
60
+
61
+ # Test without @ prefix
62
+ result2 = process_image_url(test_image_path)
63
+ print(f"✅ PASS: Successfully processed '{test_image_path}' (without @)")
64
+
65
+ # Results should be identical
66
+ if result == result2:
67
+ print(f"✅ PASS: Results are identical (@ prefix correctly removed)")
68
+ return True
69
+ else:
70
+ print(f"❌ FAIL: Results differ")
71
+ return False
72
+
73
+ except Exception as e:
74
+ print(f"❌ FAIL: {str(e)}")
75
+ return False
76
+ finally:
77
+ # Cleanup
78
+ if os.path.exists(test_image_path):
79
+ os.remove(test_image_path)
80
+
81
+
82
+ def test_nonexistent_file_with_at_prefix():
83
+ """Test error message with @ prefix on nonexistent file"""
84
+ print("\nTesting error message with @ prefix on nonexistent file...")
85
+
86
+ try:
87
+ process_image_url("@nonexistent/file.png")
88
+ print("❌ FAIL: Should have raised MinimaxRequestError")
89
+ return False
90
+ except MinimaxRequestError as e:
91
+ error_msg = str(e)
92
+ # Error message should NOT contain @ prefix
93
+ if "@" not in error_msg and "nonexistent/file.png" in error_msg:
94
+ print(f"✅ PASS: Error message correctly shows path without @")
95
+ print(f" Error: {error_msg}")
96
+ return True
97
+ else:
98
+ print(f"❌ FAIL: Error message still contains @ or incorrect path")
99
+ print(f" Error: {error_msg}")
100
+ return False
101
+ except Exception as e:
102
+ print(f"❌ FAIL: Wrong exception type: {type(e).__name__}: {str(e)}")
103
+ return False
104
+
105
+
106
+ if __name__ == "__main__":
107
+ print("=" * 60)
108
+ print("Testing @ Prefix Handling in minimax_mcp")
109
+ print("=" * 60)
110
+
111
+ results = []
112
+
113
+ results.append(("strip_at_prefix", test_strip_at_prefix()))
114
+ results.append(("process_image_url with @", test_process_image_url_with_at_prefix()))
115
+ results.append(("error message with @", test_nonexistent_file_with_at_prefix()))
116
+
117
+ print("\n" + "=" * 60)
118
+ print("Test Summary")
119
+ print("=" * 60)
120
+
121
+ all_passed = True
122
+ for name, passed in results:
123
+ status = "✅ PASS" if passed else "❌ FAIL"
124
+ print(f"{status}: {name}")
125
+ if not passed:
126
+ all_passed = False
127
+
128
+ print("=" * 60)
129
+ if all_passed:
130
+ print("✅ All tests passed!")
131
+ sys.exit(0)
132
+ else:
133
+ print("❌ Some tests failed")
134
+ sys.exit(1)
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Real API test with actual image and API key
4
+ """
5
+
6
+ import sys
7
+ import os
8
+ from dotenv import load_dotenv
9
+
10
+ # Load test environment
11
+ load_dotenv('.env.test')
12
+
13
+ # Add parent directory to path
14
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
15
+
16
+ from minimax_mcp.utils import process_image_url
17
+ from minimax_mcp.client import MinimaxAPIClient
18
+ from minimax_mcp.exceptions import MinimaxRequestError
19
+
20
+
21
+ def test_real_image_with_at_prefix(image_path: str, prompt: str = "请详细描述这张图片的内容,包括画面中的主要元素、文字信息、场景等所有细节"):
22
+ """
23
+ Test with real image using actual MiniMax API
24
+
25
+ Args:
26
+ image_path: Path to the image (can have @ prefix)
27
+ prompt: Analysis prompt
28
+ """
29
+ print("=" * 60)
30
+ print("Real Image Test with MiniMax API")
31
+ print("=" * 60)
32
+
33
+ # Get API credentials
34
+ api_key = os.getenv('MINIMAX_API_KEY')
35
+ api_host = os.getenv('MINIMAX_API_HOST')
36
+
37
+ if not api_key or api_key == 'your-api-key-here':
38
+ print("❌ Error: Please set MINIMAX_API_KEY in .env.test file")
39
+ print(" Edit .env.test and add your actual API key")
40
+ return False
41
+
42
+ if not api_host:
43
+ print("❌ Error: MINIMAX_API_HOST not set")
44
+ return False
45
+
46
+ print(f"API Host: {api_host}")
47
+ print(f"API Key: {api_key[:10]}...{api_key[-4:]}")
48
+ print()
49
+
50
+ # Test 1: With @ prefix
51
+ print("Test 1: With @ prefix")
52
+ print("-" * 60)
53
+ print(f"Original path: @{image_path}")
54
+
55
+ try:
56
+ # Process image
57
+ print("Processing image...")
58
+ processed_image_url = process_image_url(f"@{image_path}")
59
+ print(f"✅ Image processed successfully")
60
+ print(f" Data URI length: {len(processed_image_url)} chars")
61
+ print(f" First 50 chars: {processed_image_url[:50]}...")
62
+
63
+ # Call API
64
+ print("\nCalling MiniMax API...")
65
+ client = MinimaxAPIClient(api_key, api_host)
66
+
67
+ payload = {
68
+ "prompt": prompt,
69
+ "image_url": processed_image_url
70
+ }
71
+
72
+ response = client.post("/v1/coding_plan/vlm", json=payload)
73
+
74
+ content = response.get("content", "")
75
+ if content:
76
+ print(f"✅ API call successful!")
77
+ print("\n" + "=" * 60)
78
+ print("Response:")
79
+ print("=" * 60)
80
+ print(content)
81
+ print("=" * 60)
82
+ return True
83
+ else:
84
+ print("❌ No content in response")
85
+ print(f"Response: {response}")
86
+ return False
87
+
88
+ except Exception as e:
89
+ print(f"❌ Test failed: {str(e)}")
90
+ import traceback
91
+ traceback.print_exc()
92
+ return False
93
+
94
+
95
+ def test_without_at_prefix(image_path: str, prompt: str = "请详细描述这张图片的内容"):
96
+ """
97
+ Test without @ prefix for comparison
98
+ """
99
+ print("\n\nTest 2: Without @ prefix (for comparison)")
100
+ print("-" * 60)
101
+ print(f"Original path: {image_path}")
102
+
103
+ try:
104
+ processed_image_url = process_image_url(image_path)
105
+ print(f"✅ Image processed successfully")
106
+ print(f" Data URI length: {len(processed_image_url)} chars")
107
+ return True
108
+ except Exception as e:
109
+ print(f"❌ Test failed: {str(e)}")
110
+ return False
111
+
112
+
113
+ if __name__ == "__main__":
114
+ if len(sys.argv) < 2:
115
+ print("Usage: python test_real_image.py <image_path> [prompt]")
116
+ print("\nExample:")
117
+ print(" python test_real_image.py test-image.png")
118
+ print(" python test_real_image.py test-image.png \"描述这张图片\"")
119
+ print("\nNote: Image path should NOT include @ prefix in command line")
120
+ print(" The script will automatically test both with and without @")
121
+ sys.exit(1)
122
+
123
+ image_path = sys.argv[1]
124
+ prompt = sys.argv[2] if len(sys.argv) > 2 else "请详细描述这张图片的内容,包括画面中的主要元素、文字信息、场景等所有细节"
125
+
126
+ # Verify image exists
127
+ if not os.path.exists(image_path):
128
+ print(f"❌ Error: Image file not found: {image_path}")
129
+ sys.exit(1)
130
+
131
+ print(f"Testing with image: {image_path}")
132
+ print(f"Prompt: {prompt}")
133
+ print()
134
+
135
+ # Test with @ prefix (calls real API)
136
+ result1 = test_real_image_with_at_prefix(image_path, prompt)
137
+
138
+ # Test without @ prefix (just image processing)
139
+ result2 = test_without_at_prefix(image_path, prompt)
140
+
141
+ print("\n" + "=" * 60)
142
+ print("Test Summary")
143
+ print("=" * 60)
144
+ print(f"{'✅' if result1 else '❌'} Test 1: With @ prefix (real API call)")
145
+ print(f"{'✅' if result2 else '❌'} Test 2: Without @ prefix (image processing)")
146
+ print("=" * 60)
147
+
148
+ if result1 and result2:
149
+ print("✅ All tests passed!")
150
+ sys.exit(0)
151
+ else:
152
+ print("❌ Some tests failed")
153
+ sys.exit(1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luma-mcp",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Multi-model vision understanding MCP server. Supports GLM-4.5V (Zhipu), DeepSeek-OCR (SiliconFlow - Free), and Qwen3-VL-Flash (Aliyun)",
5
5
  "type": "module",
6
6
  "bin": {