luma-mcp 1.2.2 → 1.2.3
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/CHANGELOG.md +13 -1
- package/build/image-processor.d.ts +1 -2
- package/build/image-processor.d.ts.map +1 -1
- package/build/image-processor.js +55 -69
- package/build/image-processor.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.js +52 -60
- package/build/index.js.map +1 -1
- package/build/vision-client.d.ts.map +1 -1
- package/package.json +1 -1
- package/minimax_coding_plan_mcp-0.0.2/.env.test +0 -4
- package/minimax_coding_plan_mcp-0.0.2/LICENSE +0 -21
- package/minimax_coding_plan_mcp-0.0.2/PKG-INFO +0 -200
- package/minimax_coding_plan_mcp-0.0.2/README.md +0 -143
- package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/PKG-INFO +0 -200
- package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/SOURCES.txt +0 -17
- package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/dependency_links.txt +0 -1
- package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/entry_points.txt +0 -2
- package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/requires.txt +0 -20
- package/minimax_coding_plan_mcp-0.0.2/minimax_coding_plan_mcp.egg-info/top_level.txt +0 -1
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__init__.py +0 -3
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__main__.py +0 -99
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__pycache__/__init__.cpython-313.pyc +0 -0
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__pycache__/client.cpython-313.pyc +0 -0
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__pycache__/const.cpython-313.pyc +0 -0
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__pycache__/exceptions.cpython-313.pyc +0 -0
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/__pycache__/utils.cpython-313.pyc +0 -0
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/client.py +0 -104
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/const.py +0 -4
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/exceptions.py +0 -24
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/server.py +0 -169
- package/minimax_coding_plan_mcp-0.0.2/minimax_mcp/utils.py +0 -101
- package/minimax_coding_plan_mcp-0.0.2/pyproject.toml +0 -59
- package/minimax_coding_plan_mcp-0.0.2/setup.cfg +0 -4
- package/minimax_coding_plan_mcp-0.0.2/setup.py +0 -6
- package/minimax_coding_plan_mcp-0.0.2/test_at_prefix.py +0 -134
- package/minimax_coding_plan_mcp-0.0.2/test_real_image.py +0 -153
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import json
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
import sys
|
|
5
|
-
from dotenv import load_dotenv
|
|
6
|
-
import argparse
|
|
7
|
-
|
|
8
|
-
load_dotenv()
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def get_claude_config_path() -> Path | None:
|
|
12
|
-
"""Get the Claude config directory based on platform."""
|
|
13
|
-
if sys.platform == "win32":
|
|
14
|
-
path = Path(Path.home(), "AppData", "Roaming", "Claude")
|
|
15
|
-
elif sys.platform == "darwin":
|
|
16
|
-
path = Path(Path.home(), "Library", "Application Support", "Claude")
|
|
17
|
-
elif sys.platform.startswith("linux"):
|
|
18
|
-
path = Path(
|
|
19
|
-
os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"), "Claude"
|
|
20
|
-
)
|
|
21
|
-
else:
|
|
22
|
-
return None
|
|
23
|
-
|
|
24
|
-
if path.exists():
|
|
25
|
-
return path
|
|
26
|
-
return None
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def get_python_path():
|
|
30
|
-
return sys.executable
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def generate_config(api_key: str | None = None):
|
|
34
|
-
module_dir = Path(__file__).resolve().parent
|
|
35
|
-
server_path = module_dir / "server.py"
|
|
36
|
-
python_path = get_python_path()
|
|
37
|
-
|
|
38
|
-
final_api_key = api_key or os.environ.get("MINIMAX_API_KEY")
|
|
39
|
-
if not final_api_key:
|
|
40
|
-
print("Error: Minimax API key is required.")
|
|
41
|
-
print("Please either:")
|
|
42
|
-
print(" 1. Pass the API key using --api-key argument, or")
|
|
43
|
-
print(" 2. Set the MINIMAX_API_KEY environment variable, or")
|
|
44
|
-
print(" 3. Add MINIMAX_API_KEY to your .env file")
|
|
45
|
-
sys.exit(1)
|
|
46
|
-
|
|
47
|
-
config = {
|
|
48
|
-
"mcpServers": {
|
|
49
|
-
"Minimax": {
|
|
50
|
-
"command": "uvx",
|
|
51
|
-
"args": [
|
|
52
|
-
"minimax-coding-plan-mcp",
|
|
53
|
-
],
|
|
54
|
-
|
|
55
|
-
"env": {
|
|
56
|
-
"MINIMAX_API_KEY": final_api_key,
|
|
57
|
-
"MINIMAX_API_HOST": "https://api.minimax.chat"
|
|
58
|
-
},
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return config
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if __name__ == "__main__":
|
|
67
|
-
parser = argparse.ArgumentParser()
|
|
68
|
-
parser.add_argument(
|
|
69
|
-
"--print",
|
|
70
|
-
action="store_true",
|
|
71
|
-
help="Print config to screen instead of writing to file",
|
|
72
|
-
)
|
|
73
|
-
parser.add_argument(
|
|
74
|
-
"--api-key",
|
|
75
|
-
help="Minimax API key (alternatively, set MINIMAX_API_KEY environment variable)",
|
|
76
|
-
)
|
|
77
|
-
parser.add_argument(
|
|
78
|
-
"--config-path",
|
|
79
|
-
type=Path,
|
|
80
|
-
help="Custom path to Claude config directory",
|
|
81
|
-
)
|
|
82
|
-
args = parser.parse_args()
|
|
83
|
-
|
|
84
|
-
config = generate_config(args.api_key)
|
|
85
|
-
|
|
86
|
-
if args.print:
|
|
87
|
-
print(json.dumps(config, indent=2))
|
|
88
|
-
else:
|
|
89
|
-
claude_path = args.config_path if args.config_path else get_claude_config_path()
|
|
90
|
-
if claude_path is None:
|
|
91
|
-
print(
|
|
92
|
-
"Could not find Claude config path automatically. Please specify it using --config-path argument. The argument should be an absolute path of the claude_desktop_config.json file."
|
|
93
|
-
)
|
|
94
|
-
sys.exit(1)
|
|
95
|
-
|
|
96
|
-
claude_path.mkdir(parents=True, exist_ok=True)
|
|
97
|
-
print("Writing config to", claude_path / "claude_desktop_config.json")
|
|
98
|
-
with open(claude_path / "claude_desktop_config.json", "w") as f:
|
|
99
|
-
json.dump(config, f, indent=2)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"""Minimax API client base class."""
|
|
2
|
-
|
|
3
|
-
import requests
|
|
4
|
-
import urllib3
|
|
5
|
-
from typing import Any, Dict
|
|
6
|
-
from minimax_mcp.exceptions import MinimaxAuthError, MinimaxRequestError
|
|
7
|
-
|
|
8
|
-
# Disable SSL warnings for China region API
|
|
9
|
-
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
10
|
-
|
|
11
|
-
class MinimaxAPIClient:
|
|
12
|
-
"""Base client for making requests to Minimax API."""
|
|
13
|
-
|
|
14
|
-
def __init__(self, api_key: str, api_host: str):
|
|
15
|
-
"""Initialize the API client.
|
|
16
|
-
|
|
17
|
-
Args:
|
|
18
|
-
api_key: The API key for authentication
|
|
19
|
-
api_host: The API host URL
|
|
20
|
-
"""
|
|
21
|
-
self.api_key = api_key
|
|
22
|
-
self.api_host = api_host
|
|
23
|
-
self.session = requests.Session()
|
|
24
|
-
self.session.headers.update({
|
|
25
|
-
'Authorization': f'Bearer {api_key}',
|
|
26
|
-
'MM-API-Source': 'Minimax-MCP'
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
def _make_request(
|
|
30
|
-
self,
|
|
31
|
-
method: str,
|
|
32
|
-
endpoint: str,
|
|
33
|
-
**kwargs
|
|
34
|
-
) -> Dict[str, Any]:
|
|
35
|
-
"""Make an HTTP request to the Minimax API.
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
method: HTTP method (GET, POST, etc.)
|
|
39
|
-
endpoint: API endpoint path
|
|
40
|
-
**kwargs: Additional arguments to pass to requests
|
|
41
|
-
|
|
42
|
-
Returns:
|
|
43
|
-
API response data as dictionary
|
|
44
|
-
|
|
45
|
-
Raises:
|
|
46
|
-
MinimaxAuthError: If authentication fails
|
|
47
|
-
MinimaxRequestError: If the request fails
|
|
48
|
-
"""
|
|
49
|
-
url = f"{self.api_host}{endpoint}"
|
|
50
|
-
|
|
51
|
-
# Set Content-Type based on whether files are being uploaded
|
|
52
|
-
files = kwargs.get('files')
|
|
53
|
-
if not files:
|
|
54
|
-
self.session.headers['Content-Type'] = 'application/json'
|
|
55
|
-
else:
|
|
56
|
-
# Remove Content-Type header for multipart/form-data
|
|
57
|
-
# requests library will set it automatically with the correct boundary
|
|
58
|
-
self.session.headers.pop('Content-Type', None)
|
|
59
|
-
|
|
60
|
-
try:
|
|
61
|
-
# Disable SSL verification for api.minimaxi.com (China region)
|
|
62
|
-
# This is a workaround for SSL certificate verification issues
|
|
63
|
-
if 'verify' not in kwargs and 'minimaxi.com' in self.api_host:
|
|
64
|
-
kwargs['verify'] = False
|
|
65
|
-
|
|
66
|
-
response = self.session.request(method, url, **kwargs)
|
|
67
|
-
|
|
68
|
-
# Check for other HTTP errors
|
|
69
|
-
response.raise_for_status()
|
|
70
|
-
|
|
71
|
-
data = response.json()
|
|
72
|
-
|
|
73
|
-
# Check API-specific error codes
|
|
74
|
-
base_resp = data.get("base_resp", {})
|
|
75
|
-
if base_resp.get("status_code") != 0:
|
|
76
|
-
match base_resp.get("status_code"):
|
|
77
|
-
case 1004:
|
|
78
|
-
raise MinimaxAuthError(
|
|
79
|
-
f"API Error: {base_resp.get('status_msg')}, please check your API key and API host."
|
|
80
|
-
f"Trace-Id: {response.headers.get('Trace-Id')}"
|
|
81
|
-
)
|
|
82
|
-
case 2038:
|
|
83
|
-
raise MinimaxRequestError(
|
|
84
|
-
f"API Error: {base_resp.get('status_msg')}, should complete real-name verification on the open-platform(https://platform.minimaxi.com/user-center/basic-information)."
|
|
85
|
-
f"Trace-Id: {response.headers.get('Trace-Id')}"
|
|
86
|
-
)
|
|
87
|
-
case _:
|
|
88
|
-
raise MinimaxRequestError(
|
|
89
|
-
f"API Error: {base_resp.get('status_code')}-{base_resp.get('status_msg')} "
|
|
90
|
-
f"Trace-Id: {response.headers.get('Trace-Id')}"
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
return data
|
|
94
|
-
|
|
95
|
-
except requests.exceptions.RequestException as e:
|
|
96
|
-
raise MinimaxRequestError(f"Request failed: {str(e)}")
|
|
97
|
-
|
|
98
|
-
def get(self, endpoint: str, **kwargs) -> Dict[str, Any]:
|
|
99
|
-
"""Make a GET request."""
|
|
100
|
-
return self._make_request("GET", endpoint, **kwargs)
|
|
101
|
-
|
|
102
|
-
def post(self, endpoint: str, **kwargs) -> Dict[str, Any]:
|
|
103
|
-
"""Make a POST request."""
|
|
104
|
-
return self._make_request("POST", endpoint, **kwargs)
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"""Custom exceptions for Minimax MCP."""
|
|
2
|
-
|
|
3
|
-
class MinimaxAPIError(Exception):
|
|
4
|
-
"""Base exception for Minimax API errors."""
|
|
5
|
-
pass
|
|
6
|
-
|
|
7
|
-
class MinimaxAuthError(MinimaxAPIError):
|
|
8
|
-
"""Authentication related errors."""
|
|
9
|
-
pass
|
|
10
|
-
|
|
11
|
-
class MinimaxRequestError(MinimaxAPIError):
|
|
12
|
-
"""Request related errors."""
|
|
13
|
-
pass
|
|
14
|
-
|
|
15
|
-
class MinimaxTimeoutError(MinimaxAPIError):
|
|
16
|
-
"""Timeout related errors."""
|
|
17
|
-
pass
|
|
18
|
-
|
|
19
|
-
class MinimaxValidationError(MinimaxAPIError):
|
|
20
|
-
"""Validation related errors."""
|
|
21
|
-
pass
|
|
22
|
-
|
|
23
|
-
class MinimaxMcpError(MinimaxAPIError):
|
|
24
|
-
pass
|
|
@@ -1,169 +0,0 @@
|
|
|
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()
|
|
@@ -1,101 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,59 +0,0 @@
|
|
|
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"
|