minimax-coding-plan-mcp 0.0.1__py3-none-any.whl

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,202 @@
1
+ Metadata-Version: 2.4
2
+ Name: minimax-coding-plan-mcp
3
+ Version: 0.0.1
4
+ Summary: Specialized MiniMax Model Context Protocol (MCP) server designed for coding-plan users
5
+ Author-email: Roy Wu <zhengyu@minimax.chat>
6
+ License: MIT License
7
+
8
+ Copyright 2025 MiniMax AI.
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Keywords: minimax,mcp,coding-plan-search,coding-plan-vlm
29
+ Classifier: Development Status :: 4 - Beta
30
+ Classifier: Intended Audience :: Developers
31
+ Classifier: License :: OSI Approved :: MIT License
32
+ Classifier: Programming Language :: Python :: 3
33
+ Classifier: Programming Language :: Python :: 3.10
34
+ Requires-Python: >=3.10
35
+ Description-Content-Type: text/markdown
36
+ License-File: LICENSE
37
+ Requires-Dist: mcp[cli]>=1.6.0
38
+ Requires-Dist: fastapi>=0.109.2
39
+ Requires-Dist: uvicorn>=0.27.1
40
+ Requires-Dist: python-dotenv>=1.0.1
41
+ Requires-Dist: pydantic>=2.6.1
42
+ Requires-Dist: httpx>=0.28.1
43
+ Requires-Dist: fuzzywuzzy>=0.18.0
44
+ Requires-Dist: python-Levenshtein>=0.25.0
45
+ Requires-Dist: sounddevice>=0.5.1
46
+ Requires-Dist: soundfile>=0.13.1
47
+ Requires-Dist: requests>=2.31.0
48
+ Provides-Extra: dev
49
+ Requires-Dist: pre-commit>=3.6.2; extra == "dev"
50
+ Requires-Dist: ruff>=0.3.0; extra == "dev"
51
+ Requires-Dist: fastmcp>=0.4.1; extra == "dev"
52
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
53
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
54
+ Requires-Dist: twine>=6.1.0; extra == "dev"
55
+ Requires-Dist: build>=1.0.3; extra == "dev"
56
+ Dynamic: license-file
57
+
58
+ ![export](https://github.com/MiniMax-AI/MiniMax-01/raw/main/figures/MiniMaxLogo-Light.png)
59
+
60
+ <div align="center" style="line-height: 1;">
61
+ <a href="https://www.minimax.io" target="_blank" style="margin: 2px; color: var(--fgColor-default);">
62
+ <img alt="Homepage" src="https://img.shields.io/badge/_Homepage-MiniMax-FF4040?style=flat-square&labelColor=2C3E50&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNDkwLjE2IDQxMS43Ij48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6I2ZmZjt9PC9zdHlsZT48L2RlZnM+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMjMzLjQ1LDQwLjgxYTE3LjU1LDE3LjU1LDAsMSwwLTM1LjEsMFYzMzEuNTZhNDAuODIsNDAuODIsMCwwLDEtODEuNjMsMFYxNDVhMTcuNTUsMTcuNTUsMCwxLDAtMzUuMDksMHY3OS4wNmE0MC44Miw0MC44MiwwLDAsMS04MS42MywwVjE5NS40MmExMS42MywxMS42MywwLDAsMSwyMy4yNiwwdjI4LjY2YTE3LjU1LDE3LjU1LDAsMCwwLDM1LjEsMFYxNDVBNDAuODIsNDAuODIsMCwwLDEsMTQwLDE0NVYzMzEuNTZhMTcuNTUsMTcuNTUsMCwwLDAsMzUuMSwwVjIxNy41aDBWNDAuODFhNDAuODEsNDAuODEsMCwxLDEsODEuNjIsMFYyODEuNTZhMTEuNjMsMTEuNjMsMCwxLDEtMjMuMjYsMFptMjE1LjksNjMuNEE0MC44Niw0MC44NiwwLDAsMCw0MDguNTMsMTQ1VjMwMC44NWExNy41NSwxNy41NSwwLDAsMS0zNS4wOSwwdi0yNjBhNDAuODIsNDAuODIsMCwwLDAtODEuNjMsMFYzNzAuODlhMTcuNTUsMTcuNTUsMCwwLDEtMzUuMSwwVjMzMGExMS42MywxMS42MywwLDEsMC0yMy4yNiwwdjQwLjg2YTQwLjgxLDQwLjgxLDAsMCwwLDgxLjYyLDBWNDAuODFhMTcuNTUsMTcuNTUsMCwwLDEsMzUuMSwwdjI2MGE0MC44Miw0MC44MiwwLDAsMCw4MS42MywwVjE0NWExNy41NSwxNy41NSwwLDEsMSwzNS4xLDBWMjgxLjU2YTExLjYzLDExLjYzLDAsMCwwLDIzLjI2LDBWMTQ1QTQwLjg1LDQwLjg1LDAsMCwwLDQ0OS4zNSwxMDQuMjFaIi8+PC9zdmc+&logoWidth=20" style="display: inline-block; vertical-align: middle;"/>
63
+ </a>
64
+ <a href="https://arxiv.org/abs/2501.08313" target="_blank" style="margin: 2px;">
65
+ <img alt="Paper" src="https://img.shields.io/badge/πŸ“–_Paper-MiniMax--01-FF4040?style=flat-square&labelColor=2C3E50" style="display: inline-block; vertical-align: middle;"/>
66
+ </a>
67
+ <a href="https://chat.minimax.io/" target="_blank" style="margin: 2px;">
68
+ <img alt="Chat" src="https://img.shields.io/badge/_MiniMax_Chat-FF4040?style=flat-square&labelColor=2C3E50&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNDkwLjE2IDQxMS43Ij48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6I2ZmZjt9PC9zdHlsZT48L2RlZnM+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMjMzLjQ1LDQwLjgxYTE3LjU1LDE3LjU1LDAsMSwwLTM1LjEsMFYzMzEuNTZhNDAuODIsNDAuODIsMCwwLDEtODEuNjMsMFYxNDVhMTcuNTUsMTcuNTUsMCwxLDAtMzUuMDksMHY3OS4wNmE0MC44Miw0MC44MiwwLDAsMS04MS42MywwVjE5NS40MmExMS42MywxMS42MywwLDAsMSwyMy4yNiwwdjI4LjY2YTE3LjU1LDE3LjU1LDAsMCwwLDM1LjEsMFYxNDVBNDAuODIsNDAuODIsMCwwLDEsMTQwLDE0NVYzMzEuNTZhMTcuNTUsMTcuNTUsMCwwLDAsMzUuMSwwVjIxNy41aDBWNDAuODFhNDAuODEsNDAuODEsMCwxLDEsODEuNjIsMFYyODEuNTZhMTEuNjMsMTEuNjMsMCwxLDEtMjMuMjYsMFptMjE1LjksNjMuNEE0MC44Niw0MC44NiwwLDAsMCw0MDguNTMsMTQ1VjMwMC44NWExNy41NSwxNy41NSwwLDAsMS0zNS4wOSwwdi0yNjBhNDAuODIsNDAuODIsMCwwLDAtODEuNjMsMFYzNzAuODlhMTcuNTUsMTcuNTUsMCwwLDEtMzUuMSwwVjMzMGExMS42MywxMS42MywwLDEsMC0yMy4yNiwwdjQwLjg2YTQwLjgxLDQwLjgxLDAsMCwwLDgxLjYyLDBWNDAuODFhMTcuNTUsMTcuNTUsMCwwLDEsMzUuMSwwdjI2MGE0MC44Miw0MC44MiwwLDAsMCw4MS42MywwVjE0NWExNy41NSwxNy41NSwwLDEsMSwzNS4xLDBWMjgxLjU2YTExLjYzLDExLjYzLDAsMCwwLDIzLjI2LDBWMTQ1QTQwLjg1LDQwLjg1LDAsMCwwLDQ0OS4zNSwxMDQuMjFaIi8+PC9zdmc+&logoWidth=20" style="display: inline-block; vertical-align: middle;"/>
69
+ </a>
70
+ <a href="https://www.minimax.io/platform" style="margin: 2px;">
71
+ <img alt="API" src="https://img.shields.io/badge/⚑_API-Platform-FF4040?style=flat-square&labelColor=2C3E50" style="display: inline-block; vertical-align: middle;"/>
72
+ </a>
73
+ </div>
74
+ <div align="center" style="line-height: 1;">
75
+ <a href="https://huggingface.co/MiniMaxAI" target="_blank" style="margin: 2px;">
76
+ <img alt="Hugging Face" src="https://img.shields.io/badge/πŸ€—_Hugging_Face-MiniMax-FF4040?style=flat-square&labelColor=2C3E50" style="display: inline-block; vertical-align: middle;"/>
77
+ </a>
78
+ <a href="https://github.com/MiniMax-AI/MiniMax-AI.github.io/blob/main/images/wechat-qrcode.jpeg" target="_blank" style="margin: 2px;">
79
+ <img alt="WeChat" src="https://img.shields.io/badge/_WeChat-MiniMax-FF4040?style=flat-square&labelColor=2C3E50" style="display: inline-block; vertical-align: middle;"/>
80
+ </a>
81
+ <a href="https://www.modelscope.cn/organization/MiniMax" target="_blank" style="margin: 2px;">
82
+ <img alt="ModelScope" src="https://img.shields.io/badge/_ModelScope-MiniMax-FF4040?style=flat-square&labelColor=2C3E50" style="display: inline-block; vertical-align: middle;"/>
83
+ </a>
84
+ </div>
85
+ <div align="center" style="line-height: 1;">
86
+ <a href="https://github.com/MiniMax-AI/MiniMax-MCP/blob/main/LICENSE" style="margin: 2px;">
87
+ <img alt="Code License" src="https://img.shields.io/badge/_Code_License-MIT-FF4040?style=flat-square&labelColor=2C3E50" style="display: inline-block; vertical-align: middle;"/>
88
+ </a>
89
+ </div>
90
+
91
+ <p align="center">
92
+ Specialized MiniMax Model Context Protocol (MCP) server designed for <b>coding-plan</b> users, featuring AI-powered search and vision analysis APIs optimized for code development workflows. Unlike the standard <a href="https://github.com/MiniMax-AI/MiniMax-MCP">MiniMax-MCP</a>, this version provides coding-specific tools (for examples: <code>coding_plan_search</code> and <code>coding_plan_vlm</code>) that integrate seamlessly with MCP clients like <a href="https://www.anthropic.com/claude">Claude Desktop</a>, <a href="https://www.cursor.so">Cursor</a>, <a href="https://codeium.com/windsurf">Windsurf</a>, <a href="https://github.com/openai/openai-agents-python">OpenAI Agents</a> and others to enhance your coding experience.
93
+ </p>
94
+
95
+ ## Documentation
96
+ - [δΈ­ζ–‡ζ–‡ζ‘£](README-CN.md)
97
+
98
+
99
+ ## Quickstart with MCP Client
100
+ 1. Get your API key from [MiniMax](https://www.minimax.io/platform/user-center/basic-information/interface-key).
101
+ 2. Install `uv` (Python package manager), install with `curl -LsSf https://astral.sh/uv/install.sh | sh` or see the `uv` [repo](https://github.com/astral-sh/uv) for additional install methods.
102
+ 3. **Important**: The API host and key vary by region and must match; otherwise, you'll encounter an `Invalid API key` error.
103
+
104
+ |Region| Global | Mainland |
105
+ |:--|:-----|:-----|
106
+ |MINIMAX_API_KEY| go get from [MiniMax Global](https://www.minimax.io/platform/user-center/basic-information/interface-key) | go get from [MiniMax](https://platform.minimaxi.com/user-center/basic-information/interface-key) |
107
+ |MINIMAX_API_HOST| https://api.minimax.io | https://api.minimaxi.com |
108
+
109
+
110
+ ### Claude Desktop
111
+ Go to `Claude > Settings > Developer > Edit Config > claude_desktop_config.json` to include the following:
112
+
113
+ ```
114
+ {
115
+ "mcpServers": {
116
+ "MiniMax": {
117
+ "command": "uvx",
118
+ "args": [
119
+ "minimax-coding-plan-mcp",
120
+ "-y"
121
+ ],
122
+ "env": {
123
+ "MINIMAX_API_KEY": "insert-your-api-key-here",
124
+ "MINIMAX_MCP_BASE_PATH": "local-output-dir-path, such as /User/xxx/Desktop",
125
+ "MINIMAX_API_HOST": "api host, https://api.minimax.io | https://api.minimaxi.com",
126
+ "MINIMAX_API_RESOURCE_MODE": "optional, [url|local], url is default, audio/image/video are downloaded locally or provided in URL format"
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ ```
133
+ ⚠️ Warning: The API key needs to match the host. If an error "API Error: invalid api key" occurs, please check your api host:
134
+ - Global Host:`https://api.minimax.io`
135
+ - Mainland Host:`https://api.minimaxi.com`
136
+
137
+ If you're using Windows, you will have to enable "Developer Mode" in Claude Desktop to use the MCP server. Click "Help" in the hamburger menu in the top left and select "Enable Developer Mode".
138
+
139
+
140
+ ### Cursor
141
+ Go to `Cursor -> Preferences -> Cursor Settings -> MCP -> Add new global MCP Server` to add above config.
142
+
143
+ That's it. Your MCP client can now interact with MiniMax through these tools:
144
+
145
+ ## Transport
146
+ We support two transport types: stdio and sse.
147
+ | stdio | SSE |
148
+ |:-----|:-----|
149
+ | Run locally | Can be deployed locally or in the cloud |
150
+ | Communication through `stdout` | Communication through `network` |
151
+ | Input: Supports processing `local files` or valid `URL` resources | Input: When deployed in the cloud, it is recommended to use `URL` for input |
152
+
153
+ ## Available Tools
154
+ | tool | description |
155
+ |-|-|
156
+ |`coding_plan_search`|Performs web searches and returns organic search results along with related search queries|
157
+ |`coding_plan_vlm`|Analyzes images with AI based on text prompts, extracts information and answers questions about images|
158
+
159
+ ## Release Notes
160
+
161
+ ### November 20, 2025
162
+
163
+ #### πŸ†• What's New
164
+ - **Web Search**: New `coding_plan_search` tool - perform web searches and get organic results with related search queries
165
+ - **Vision Language Model**: New `coding_plan_vlm` tool - analyze images using AI based on text prompts
166
+
167
+ #### πŸ“ˆ Features
168
+ - `coding_plan_search` - Search the web and get structured results including titles, links, snippets, and related searches
169
+ - `coding_plan_vlm` - Analyze images from URLs or local files, supporting JPEG, PNG, GIF, and WebP formats
170
+
171
+ ## FAQ
172
+ ### 1. invalid api key
173
+ Please ensure your API key and API host are regionally aligned
174
+ |Region| Global | Mainland |
175
+ |:--|:-----|:-----|
176
+ |MINIMAX_API_KEY| go get from [MiniMax Global](https://www.minimax.io/platform/user-center/basic-information/interface-key) | go get from [MiniMax](https://platform.minimaxi.com/user-center/basic-information/interface-key) |
177
+ |MINIMAX_API_HOST| https://api.minimax.io | https://api.minimaxi.com |
178
+
179
+ ### 2. spawn uvx ENOENT
180
+ Please confirm its absolute path by running this command in your terminal:
181
+ ```sh
182
+ which uvx
183
+ ```
184
+ Once you obtain the absolute path (e.g., /usr/local/bin/uvx), update your configuration to use that path (e.g., "command": "/usr/local/bin/uvx").
185
+
186
+
187
+
188
+ ## Example usage
189
+
190
+ ⚠️ Warning: Using these tools may incur costs.
191
+
192
+ ### 1. Web Search
193
+ Use the `coding_plan_search` tool to search for information on the web:
194
+
195
+ <img src="https://cdn.hailuoai.video/moss/prod/2025-11-20-15/user/multi_chat_file/99ff15e3-e5d6-48e2-8216-38c6abf03c71.image/png" style="display:
196
+ inline-block; vertical-align: middle; "/>
197
+
198
+ ### 2. Image Analysis
199
+ Use the `coding_plan_vlm` tool to analyze images with AI:
200
+
201
+ <img src="https://cdn.hailuoai.video/moss/prod/2025-11-20-15/user/multi_chat_file/1f8174a2-68d5-4576-b886-86933eab5280.image/png" style="display:
202
+ inline-block; vertical-align: middle; "/>
@@ -0,0 +1,13 @@
1
+ minimax_coding_plan_mcp-0.0.1.dist-info/licenses/LICENSE,sha256=qROMAfPCJkGsj4_i8-x13gtKBJT_gejaQ7PMIJGTWsM,1064
2
+ minimax_mcp/__init__.py,sha256=CKIi_HLF6Kg0jwf_Mq4H50_YqbadAzELljOlfvBoFAI,69
3
+ minimax_mcp/__main__.py,sha256=t-bxkqivbV-ri9pZADozQS2eUpjq5_cWTJXuG-vclmQ,3021
4
+ minimax_mcp/client.py,sha256=CNQRaiDoplLOueK29YsefN_kZLFLr_BZd-CLSwW2a-M,3675
5
+ minimax_mcp/const.py,sha256=mPzsI7CgHw9uaxJ4egkRAhmYaF8yCPmEBWaBepGE5KU,830
6
+ minimax_mcp/exceptions.py,sha256=f02y8FAKo7WS9jqhQD1_hFkPHWFX4-j5nEvZ_rBmFCU,547
7
+ minimax_mcp/server.py,sha256=wXW_4XEThyrqthKyLDQSCqpyvb33zd-LBrKbiw5raXE,5484
8
+ minimax_mcp/utils.py,sha256=YndfmVoeFyIHmyPcMe2uQke0C-r17eq288_2wRb3PUs,8504
9
+ minimax_coding_plan_mcp-0.0.1.dist-info/METADATA,sha256=NaxK9rYdwg2Eoy2dqGI6lsjsfywuLS48Zl-aQycJ0JY,12902
10
+ minimax_coding_plan_mcp-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ minimax_coding_plan_mcp-0.0.1.dist-info/entry_points.txt,sha256=nBQn7kfqXxlmJCWcPmnLA9nyy-wGLUuI0p4daZ6_OHg,68
12
+ minimax_coding_plan_mcp-0.0.1.dist-info/top_level.txt,sha256=PQ7pnbym7cuF8XXvQ-yLu2czj9EDuAhkTbbwtoeEKsA,12
13
+ minimax_coding_plan_mcp-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ minimax-coding-plan-mcp = minimax_mcp.server:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright 2025 MiniMax AI.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ minimax_mcp
@@ -0,0 +1,3 @@
1
+ """Minimax Coding Plan MCP Server package."""
2
+
3
+ __version__ = "0.0.1"
@@ -0,0 +1,100 @@
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_MCP_BASE_PATH": "",
58
+ "MINIMAX_API_HOST": "https://api.minimax.chat",
59
+ },
60
+ }
61
+ }
62
+ }
63
+
64
+ return config
65
+
66
+
67
+ if __name__ == "__main__":
68
+ parser = argparse.ArgumentParser()
69
+ parser.add_argument(
70
+ "--print",
71
+ action="store_true",
72
+ help="Print config to screen instead of writing to file",
73
+ )
74
+ parser.add_argument(
75
+ "--api-key",
76
+ help="Minimax API key (alternatively, set MINIMAX_API_KEY environment variable)",
77
+ )
78
+ parser.add_argument(
79
+ "--config-path",
80
+ type=Path,
81
+ help="Custom path to Claude config directory",
82
+ )
83
+ args = parser.parse_args()
84
+
85
+ config = generate_config(args.api_key)
86
+
87
+ if args.print:
88
+ print(json.dumps(config, indent=2))
89
+ else:
90
+ claude_path = args.config_path if args.config_path else get_claude_config_path()
91
+ if claude_path is None:
92
+ print(
93
+ "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."
94
+ )
95
+ sys.exit(1)
96
+
97
+ claude_path.mkdir(parents=True, exist_ok=True)
98
+ print("Writing config to", claude_path / "claude_desktop_config.json")
99
+ with open(claude_path / "claude_desktop_config.json", "w") as f:
100
+ json.dump(config, f, indent=2)
minimax_mcp/client.py ADDED
@@ -0,0 +1,95 @@
1
+ """Minimax API client base class."""
2
+
3
+ import requests
4
+ from typing import Any, Dict
5
+ from minimax_mcp.exceptions import MinimaxAuthError, MinimaxRequestError
6
+
7
+ class MinimaxAPIClient:
8
+ """Base client for making requests to Minimax API."""
9
+
10
+ def __init__(self, api_key: str, api_host: str):
11
+ """Initialize the API client.
12
+
13
+ Args:
14
+ api_key: The API key for authentication
15
+ api_host: The API host URL
16
+ """
17
+ self.api_key = api_key
18
+ self.api_host = api_host
19
+ self.session = requests.Session()
20
+ self.session.headers.update({
21
+ 'Authorization': f'Bearer {api_key}',
22
+ 'MM-API-Source': 'Minimax-MCP'
23
+ })
24
+
25
+ def _make_request(
26
+ self,
27
+ method: str,
28
+ endpoint: str,
29
+ **kwargs
30
+ ) -> Dict[str, Any]:
31
+ """Make an HTTP request to the Minimax API.
32
+
33
+ Args:
34
+ method: HTTP method (GET, POST, etc.)
35
+ endpoint: API endpoint path
36
+ **kwargs: Additional arguments to pass to requests
37
+
38
+ Returns:
39
+ API response data as dictionary
40
+
41
+ Raises:
42
+ MinimaxAuthError: If authentication fails
43
+ MinimaxRequestError: If the request fails
44
+ """
45
+ url = f"{self.api_host}{endpoint}"
46
+
47
+ # Set Content-Type based on whether files are being uploaded
48
+ files = kwargs.get('files')
49
+ if not files:
50
+ self.session.headers['Content-Type'] = 'application/json'
51
+ else:
52
+ # Remove Content-Type header for multipart/form-data
53
+ # requests library will set it automatically with the correct boundary
54
+ self.session.headers.pop('Content-Type', None)
55
+
56
+ try:
57
+ response = self.session.request(method, url, **kwargs)
58
+
59
+ # Check for other HTTP errors
60
+ response.raise_for_status()
61
+
62
+ data = response.json()
63
+
64
+ # Check API-specific error codes
65
+ base_resp = data.get("base_resp", {})
66
+ if base_resp.get("status_code") != 0:
67
+ match base_resp.get("status_code"):
68
+ case 1004:
69
+ raise MinimaxAuthError(
70
+ f"API Error: {base_resp.get('status_msg')}, please check your API key and API host."
71
+ f"Trace-Id: {response.headers.get('Trace-Id')}"
72
+ )
73
+ case 2038:
74
+ raise MinimaxRequestError(
75
+ 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)."
76
+ f"Trace-Id: {response.headers.get('Trace-Id')}"
77
+ )
78
+ case _:
79
+ raise MinimaxRequestError(
80
+ f"API Error: {base_resp.get('status_code')}-{base_resp.get('status_msg')} "
81
+ f"Trace-Id: {response.headers.get('Trace-Id')}"
82
+ )
83
+
84
+ return data
85
+
86
+ except requests.exceptions.RequestException as e:
87
+ raise MinimaxRequestError(f"Request failed: {str(e)}")
88
+
89
+ def get(self, endpoint: str, **kwargs) -> Dict[str, Any]:
90
+ """Make a GET request."""
91
+ return self._make_request("GET", endpoint, **kwargs)
92
+
93
+ def post(self, endpoint: str, **kwargs) -> Dict[str, Any]:
94
+ """Make a POST request."""
95
+ return self._make_request("POST", endpoint, **kwargs)
minimax_mcp/const.py ADDED
@@ -0,0 +1,30 @@
1
+ # speech model default values
2
+ DEFAULT_VOICE_ID = "female-shaonv"
3
+ DEFAULT_SPEECH_MODEL = "speech-02-hd"
4
+ DEFAULT_MUSIC_MODEL = "music-1.5"
5
+ DEFAULT_SPEED = 1.0
6
+ DEFAULT_VOLUME = 1.0
7
+ DEFAULT_PITCH = 0
8
+ DEFAULT_EMOTION = "happy"
9
+ DEFAULT_SAMPLE_RATE = 32000
10
+ DEFAULT_BITRATE = 128000
11
+ DEFAULT_CHANNEL = 1
12
+ DEFAULT_FORMAT = "mp3"
13
+ DEFAULT_LANGUAGE_BOOST = "auto"
14
+
15
+ # video model default values
16
+ DEFAULT_T2V_MODEL = "T2V-01"
17
+
18
+ # image model default values
19
+ DEFAULT_T2I_MODEL = "image-01"
20
+
21
+ # ENV variables
22
+ ENV_MINIMAX_API_KEY = "MINIMAX_API_KEY"
23
+ ENV_MINIMAX_API_HOST = "MINIMAX_API_HOST"
24
+ ENV_MINIMAX_MCP_BASE_PATH = "MINIMAX_MCP_BASE_PATH"
25
+ ENV_RESOURCE_MODE = "MINIMAX_API_RESOURCE_MODE"
26
+
27
+ RESOURCE_MODE_LOCAL = "local" # save resource to local file system
28
+ RESOURCE_MODE_URL = "url" # provide resource url
29
+
30
+ ENV_FASTMCP_LOG_LEVEL = "FASTMCP_LOG_LEVEL"
@@ -0,0 +1,24 @@
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
minimax_mcp/server.py ADDED
@@ -0,0 +1,170 @@
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
+ base_path = os.getenv(ENV_MINIMAX_MCP_BASE_PATH) or "~/Desktop"
30
+ api_host = os.getenv(ENV_MINIMAX_API_HOST)
31
+ resource_mode = os.getenv(ENV_RESOURCE_MODE) or RESOURCE_MODE_URL
32
+ fastmcp_log_level = os.getenv(ENV_FASTMCP_LOG_LEVEL) or "WARNING"
33
+
34
+ if not api_key:
35
+ raise ValueError("MINIMAX_API_KEY environment variable is required")
36
+ if not api_host:
37
+ raise ValueError("MINIMAX_API_HOST environment variable is required")
38
+
39
+ mcp = FastMCP("Minimax",log_level=fastmcp_log_level)
40
+ api_client = MinimaxAPIClient(api_key, api_host)
41
+
42
+ @mcp.tool(
43
+ description="""Search for information using a search query.
44
+
45
+ This tool performs web searches and returns organic search results along with related search queries.
46
+
47
+ COST WARNING: This tool makes an API call to Minimax which may incur costs. Only use when explicitly requested by the user.
48
+
49
+ Args:
50
+ query (str): The search query string.
51
+
52
+ Returns:
53
+ Text content with search results in JSON format. The response structure is:
54
+ {
55
+ "organic": [
56
+ {
57
+ "title": "string - The title of the search result",
58
+ "link": "string - The URL link to the result",
59
+ "snippet": "string - A brief description or excerpt",
60
+ "date": "string - The date of the result"
61
+ }
62
+ ],
63
+ "related_searches": [
64
+ {
65
+ "query": "string - A related search query suggestion"
66
+ }
67
+ ],
68
+ "base_resp": {
69
+ "status_code": "int - Response status code",
70
+ "status_msg": "string - Response status message"
71
+ }
72
+ }
73
+ """
74
+ )
75
+ def coding_plan_search(
76
+ query: str,
77
+ ) -> TextContent:
78
+ try:
79
+ if not query:
80
+ raise MinimaxRequestError("Query is required")
81
+
82
+ # Build request payload
83
+ payload = {
84
+ "q": query
85
+ }
86
+
87
+ # Call search API
88
+ response_data = api_client.post("/v1/coding_plan/search", json=payload)
89
+
90
+ # Return JSON dump of response data
91
+ return TextContent(
92
+ type="text",
93
+ text=json.dumps(response_data, ensure_ascii=False, indent=2)
94
+ )
95
+
96
+ except MinimaxAPIError as e:
97
+ return TextContent(
98
+ type="text",
99
+ text=f"Failed to perform search: {str(e)}"
100
+ )
101
+
102
+
103
+ @mcp.tool(
104
+ description="""Analyze an image with AI based on your text prompt.
105
+
106
+ This tool analyzes images and answers questions or extracts information based on your prompt.
107
+
108
+ COST WARNING: This tool makes an API call to Minimax which may incur costs. Only use when explicitly requested by the user.
109
+
110
+ Args:
111
+ prompt (str): The text prompt describing what you want to analyze or extract from the image.
112
+ image_source (str): The source location of the image to analyze.
113
+ Accepts:
114
+ - HTTP/HTTPS URL: "https://example.com/image.jpg"
115
+ - Local file path: "/path/to/image.png"
116
+ Supported formats: JPEG, PNG, GIF, WebP
117
+
118
+ Returns:
119
+ Text content with the VLM analysis result containing the content field.
120
+ """
121
+ )
122
+ def coding_plan_vlm(
123
+ prompt: str,
124
+ image_source: str,
125
+ ) -> TextContent:
126
+ try:
127
+ if not prompt:
128
+ raise MinimaxRequestError("Prompt is required")
129
+ if not image_source:
130
+ raise MinimaxRequestError("Image source is required")
131
+
132
+ # Process image_source: convert HTTP URL or local file to base64, or pass through existing base64
133
+ processed_image_url = process_image_url(image_source)
134
+
135
+ # Build request payload
136
+ payload = {
137
+ "prompt": prompt,
138
+ "image_url": processed_image_url
139
+ }
140
+
141
+ # Call VLM API
142
+ response_data = api_client.post("/v1/coding_plan/vlm", json=payload)
143
+
144
+ # Extract content from response
145
+ content = response_data.get("content", "")
146
+
147
+ if not content:
148
+ raise MinimaxRequestError("No content returned from VLM API")
149
+
150
+ # Return the content
151
+ return TextContent(
152
+ type="text",
153
+ text=content
154
+ )
155
+
156
+ except MinimaxAPIError as e:
157
+ return TextContent(
158
+ type="text",
159
+ text=f"Failed to perform VLM analysis: {str(e)}"
160
+ )
161
+
162
+
163
+ def main():
164
+ print("Starting Minimax MCP server")
165
+ """Run the Minimax MCP server"""
166
+ mcp.run()
167
+
168
+
169
+ if __name__ == "__main__":
170
+ main()
minimax_mcp/utils.py ADDED
@@ -0,0 +1,251 @@
1
+ import os
2
+ from pathlib import Path
3
+ from datetime import datetime
4
+ from fuzzywuzzy import fuzz
5
+ import shutil
6
+ import subprocess
7
+ import base64
8
+ import requests
9
+ from typing import Iterator, Union
10
+ from minimax_mcp.const import *
11
+ from minimax_mcp.exceptions import MinimaxMcpError, MinimaxRequestError
12
+
13
+
14
+ def is_file_writeable(path: Path) -> bool:
15
+ if path.exists():
16
+ return os.access(path, os.W_OK)
17
+ parent_dir = path.parent
18
+ return os.access(parent_dir, os.W_OK)
19
+
20
+
21
+ def build_output_file(
22
+ tool: str, text: str, output_path: Path, extension: str, full_id: bool = False
23
+ ) -> Path:
24
+ id = text if full_id else text[:10]
25
+
26
+ output_file_name = f"{tool}_{id.replace(' ', '_')}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{extension}"
27
+ return output_path / output_file_name
28
+
29
+
30
+ def build_output_path(
31
+ output_directory: str | None, base_path: str | None = None, is_test: bool = False
32
+ ) -> Path:
33
+ # Set default base_path to desktop if not provided
34
+ if base_path is None:
35
+ base_path = str(Path.home() / "Desktop")
36
+
37
+ # Handle output path based on output_directory
38
+ if output_directory is None:
39
+ output_path = Path(os.path.expanduser(base_path))
40
+ elif not os.path.isabs(os.path.expanduser(output_directory)):
41
+ output_path = Path(os.path.expanduser(base_path)) / Path(output_directory)
42
+ else:
43
+ output_path = Path(os.path.expanduser(output_directory))
44
+
45
+ # Safety checks and directory creation
46
+ if is_test:
47
+ return output_path
48
+ if not is_file_writeable(output_path):
49
+ raise MinimaxMcpError(f"Directory ({output_path}) is not writeable")
50
+ output_path.mkdir(parents=True, exist_ok=True)
51
+ return output_path
52
+
53
+
54
+ def find_similar_filenames(
55
+ target_file: str, directory: Path, threshold: int = 70
56
+ ) -> list[tuple[str, int]]:
57
+ """
58
+ Find files with names similar to the target file using fuzzy matching.
59
+
60
+ Args:
61
+ target_file (str): The reference filename to compare against
62
+ directory (str): Directory to search in (defaults to current directory)
63
+ threshold (int): Similarity threshold (0 to 100, where 100 is identical)
64
+
65
+ Returns:
66
+ list: List of similar filenames with their similarity scores
67
+ """
68
+ target_filename = os.path.basename(target_file)
69
+ similar_files = []
70
+ for root, _, files in os.walk(directory):
71
+ for filename in files:
72
+ if (
73
+ filename == target_filename
74
+ and os.path.join(root, filename) == target_file
75
+ ):
76
+ continue
77
+ similarity = fuzz.token_sort_ratio(target_filename, filename)
78
+
79
+ if similarity >= threshold:
80
+ file_path = Path(root) / filename
81
+ similar_files.append((file_path, similarity))
82
+
83
+ similar_files.sort(key=lambda x: x[1], reverse=True)
84
+
85
+ return similar_files
86
+
87
+
88
+ def try_find_similar_files(
89
+ filename: str, directory: Path, take_n: int = 5
90
+ ) -> list[Path]:
91
+ similar_files = find_similar_filenames(filename, directory)
92
+ if not similar_files:
93
+ return []
94
+
95
+ filtered_files = []
96
+
97
+ for path, _ in similar_files[:take_n]:
98
+ if check_audio_file(path):
99
+ filtered_files.append(path)
100
+
101
+ return filtered_files
102
+
103
+
104
+ def check_audio_file(path: Path) -> bool:
105
+ audio_extensions = {
106
+ ".wav",
107
+ ".mp3",
108
+ ".m4a",
109
+ ".aac",
110
+ ".ogg",
111
+ ".flac",
112
+ ".mp4",
113
+ ".avi",
114
+ ".mov",
115
+ ".wmv",
116
+ }
117
+ return path.suffix.lower() in audio_extensions
118
+
119
+
120
+ def process_input_file(file_path: str, audio_content_check: bool = True) -> Path:
121
+ if not os.path.isabs(file_path) and not os.environ.get(ENV_MINIMAX_MCP_BASE_PATH):
122
+ raise MinimaxMcpError(
123
+ "File path must be an absolute path if MINIMAX_MCP_BASE_PATH is not set"
124
+ )
125
+ path = Path(file_path)
126
+ if not path.exists() and path.parent.exists():
127
+ parent_directory = path.parent
128
+ similar_files = try_find_similar_files(path.name, parent_directory)
129
+ similar_files_formatted = ",".join([str(file) for file in similar_files])
130
+ if similar_files:
131
+ raise MinimaxMcpError(
132
+ f"File ({path}) does not exist. Did you mean any of these files: {similar_files_formatted}?"
133
+ )
134
+ raise MinimaxMcpError(f"File ({path}) does not exist")
135
+ elif not path.exists():
136
+ raise MinimaxMcpError(f"File ({path}) does not exist")
137
+ elif not path.is_file():
138
+ raise MinimaxMcpError(f"File ({path}) is not a file")
139
+
140
+ if audio_content_check and not check_audio_file(path):
141
+ raise MinimaxMcpError(f"File ({path}) is not an audio or video file")
142
+ return path
143
+
144
+
145
+ def is_installed(lib_name: str) -> bool:
146
+ return shutil.which(lib_name) is not None
147
+
148
+
149
+ def play(
150
+ audio: Union[bytes, Iterator[bytes]]
151
+ ) -> None:
152
+ if isinstance(audio, Iterator):
153
+ audio = b"".join(audio)
154
+
155
+ if not is_installed("ffplay"):
156
+ message = (
157
+ "ffplay from ffmpeg not found, necessary to play audio. "
158
+ "mac: install it with 'brew install ffmpeg'. "
159
+ "linux or windows: install it from https://ffmpeg.org/"
160
+ )
161
+ raise ValueError(message)
162
+
163
+ args = ["ffplay", "-autoexit", "-", "-nodisp"]
164
+ proc = subprocess.Popen(
165
+ args=args,
166
+ stdout=subprocess.PIPE,
167
+ stdin=subprocess.PIPE,
168
+ stderr=subprocess.PIPE,
169
+ )
170
+ out, err = proc.communicate(input=audio)
171
+
172
+ proc.poll()
173
+
174
+
175
+ def process_image_url(image_url: str) -> str:
176
+ """
177
+ Process image URL and convert to base64 data URL format.
178
+
179
+ This function handles three types of image inputs:
180
+ 1. HTTP/HTTPS URLs: Downloads the image and converts to base64
181
+ 2. Base64 data URLs: Passes through as-is
182
+ 3. Local file paths: Reads the file and converts to base64
183
+
184
+ Args:
185
+ image_url (str): The image URL, data URL, or local file path
186
+
187
+ Returns:
188
+ str: Base64 data URL in format "data:image/{format};base64,{data}"
189
+
190
+ Raises:
191
+ MinimaxRequestError: If image cannot be downloaded, read, or processed
192
+ """
193
+ # If already in base64 data URL format, pass through
194
+ if image_url.startswith("data:"):
195
+ return image_url
196
+
197
+ # Handle HTTP/HTTPS URLs
198
+ if image_url.startswith(("http://", "https://")):
199
+ try:
200
+ image_response = requests.get(image_url)
201
+ image_response.raise_for_status()
202
+ image_data = image_response.content
203
+
204
+ # Detect image format from content-type header
205
+ content_type = image_response.headers.get('content-type', '').lower()
206
+ if 'jpeg' in content_type or 'jpg' in content_type:
207
+ image_format = 'jpeg'
208
+ elif 'png' in content_type:
209
+ image_format = 'png'
210
+ elif 'gif' in content_type:
211
+ image_format = 'gif'
212
+ elif 'webp' in content_type:
213
+ image_format = 'webp'
214
+ else:
215
+ # Default to jpeg if cannot detect
216
+ image_format = 'jpeg'
217
+
218
+ # Convert to base64 data URL
219
+ base64_data = base64.b64encode(image_data).decode('utf-8')
220
+ return f"data:image/{image_format};base64,{base64_data}"
221
+
222
+ except requests.RequestException as e:
223
+ raise MinimaxRequestError(f"Failed to download image from URL: {str(e)}")
224
+
225
+ # Handle local file paths
226
+ else:
227
+ if not os.path.exists(image_url):
228
+ raise MinimaxRequestError(f"Local image file does not exist: {image_url}")
229
+
230
+ try:
231
+ with open(image_url, "rb") as f:
232
+ image_data = f.read()
233
+
234
+ # Detect image format from file extension
235
+ image_format = 'jpeg' # Default
236
+ if image_url.lower().endswith('.png'):
237
+ image_format = 'png'
238
+ elif image_url.lower().endswith('.gif'):
239
+ image_format = 'gif'
240
+ elif image_url.lower().endswith('.webp'):
241
+ image_format = 'webp'
242
+ elif image_url.lower().endswith(('.jpg', '.jpeg')):
243
+ image_format = 'jpeg'
244
+
245
+ base64_data = base64.b64encode(image_data).decode('utf-8')
246
+ return f"data:image/{image_format};base64,{base64_data}"
247
+
248
+ except IOError as e:
249
+ raise MinimaxRequestError(f"Failed to read local image file: {str(e)}")
250
+
251
+