@mseep/mmx-mcp-server 1.0.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.
- package/LICENSE +21 -0
- package/README.md +140 -0
- package/README_EN.md +140 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +538 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 bingking
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# mmx-mcp-server
|
|
2
|
+
|
|
3
|
+
一个统一的 MiniMax 全模态 MCP Server,基于 `mmx` CLI 封装,支持文本、搜索、图像理解、图像生成、语音合成、视频生成、音乐生成和配额查询。
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
> [English README](README_EN.md)
|
|
8
|
+
|
|
9
|
+
## 快速开始(一句话配置)
|
|
10
|
+
|
|
11
|
+
如果你使用的是 Claude Code、OpenCode 或其他支持 MCP 的 AI CLI 工具,直接把下面这段话丢给 AI:
|
|
12
|
+
|
|
13
|
+
> 帮我安装并配置 mmx-mcp-server:
|
|
14
|
+
> 1. 全局安装 mmx-cli(`npm install -g mmx-cli`)
|
|
15
|
+
> 2. 克隆 https://github.com/zth0828/mmx-mcp-server 到任意目录,执行 `npm install && npm run build`
|
|
16
|
+
> 3. 在我的 MCP 配置文件(如 `~/.claude/settings.json`)里添加 `mmx` server,指向构建产物 `dist/index.js`
|
|
17
|
+
> 4. 向我要 MiniMax API Key,并通过 `env.MINIMAX_API_KEY` 注入
|
|
18
|
+
> 5. 完成后告诉我配置结果
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 手动安装
|
|
23
|
+
|
|
24
|
+
### 1. 安装 mmx CLI(如果尚未安装)
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -g mmx-cli
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
验证安装:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
mmx --version
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. 安装并构建 MCP Server
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/zth0828/mmx-mcp-server.git
|
|
40
|
+
cd mmx-mcp-server
|
|
41
|
+
npm install
|
|
42
|
+
npm run build
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 3. 配置 API Key
|
|
46
|
+
|
|
47
|
+
在 MCP 客户端配置(如 `~/.claude/settings.json`)中添加:
|
|
48
|
+
|
|
49
|
+
#### 方式一:环境变量传 API Key(推荐)
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"mmx": {
|
|
55
|
+
"command": "node",
|
|
56
|
+
"args": ["/path/to/mmx-mcp-server/dist/index.js"],
|
|
57
|
+
"env": {
|
|
58
|
+
"MINIMAX_API_KEY": "sk-xxxxx"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### 方式二:命令行参数传 API Key
|
|
66
|
+
|
|
67
|
+
适合不方便写 `env` 的 MCP 客户端:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"mmx": {
|
|
73
|
+
"command": "node",
|
|
74
|
+
"args": [
|
|
75
|
+
"/path/to/mmx-mcp-server/dist/index.js",
|
|
76
|
+
"--api-key",
|
|
77
|
+
"sk-xxxxx"
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### 方式三:依赖 `mmx` CLI 本地登录
|
|
85
|
+
|
|
86
|
+
如果前两种方式都没配,Server 会自动回退到 `mmx` CLI 自身的认证状态(需提前执行 `mmx auth login --api-key sk-xxxxx`)。这种方式仅适合本机使用。
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### 自定义输出目录(可选)
|
|
91
|
+
|
|
92
|
+
默认情况下,生成的图片、视频、音乐、语音会自动保存到 **当前工作目录** 下的子文件夹:
|
|
93
|
+
|
|
94
|
+
- `mmx_image/` — 图片
|
|
95
|
+
- `mmx_video/` — 视频
|
|
96
|
+
- `mmx_music/` — 音乐
|
|
97
|
+
- `mmx_speech/` — 语音
|
|
98
|
+
|
|
99
|
+
如果你希望统一放到其他位置,可以通过环境变量 `MMX_OUTPUT_DIR` 修改:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"mmx": {
|
|
105
|
+
"command": "node",
|
|
106
|
+
"args": ["/path/to/mmx-mcp-server/dist/index.js"],
|
|
107
|
+
"env": {
|
|
108
|
+
"MINIMAX_API_KEY": "sk-xxxxx",
|
|
109
|
+
"MMX_OUTPUT_DIR": "/Users/xxx/Downloads/mmx-output"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
> 不配置 `MMX_OUTPUT_DIR` 也完全不影响使用,文件会自动落在当前项目目录下。
|
|
117
|
+
|
|
118
|
+
每次调用时也可以通过 `out` 参数指定单个文件的输出路径。
|
|
119
|
+
|
|
120
|
+
## 提供的 Tools
|
|
121
|
+
|
|
122
|
+
| Tool | 说明 |
|
|
123
|
+
|------|------|
|
|
124
|
+
| `mmx_search` | 网页搜索 |
|
|
125
|
+
| `mmx_vision_describe` | 图像理解(支持自定义分析指令,如 UI critique) |
|
|
126
|
+
| `mmx_text_chat` | 文本对话(支持 system prompt、JSON 模式) |
|
|
127
|
+
| `mmx_image_generate` | 图像生成(支持 `out` 指定路径) |
|
|
128
|
+
| `mmx_speech_synthesize` | 语音合成 TTS(支持 `out` 指定路径) |
|
|
129
|
+
| `mmx_video_generate` | 视频生成(支持 `out` 指定路径) |
|
|
130
|
+
| `mmx_music_generate` | 音乐生成(支持 `out` 指定路径) |
|
|
131
|
+
| `mmx_quota_show` | 查看配额 |
|
|
132
|
+
|
|
133
|
+
## 使用示例
|
|
134
|
+
|
|
135
|
+
配置完成后,你可以在 Claude Code 中直接说:
|
|
136
|
+
|
|
137
|
+
> "帮我搜索 MiniMax 的最新新闻"
|
|
138
|
+
> "分析一下这个 UI 设计图,给出优化建议"
|
|
139
|
+
> "生成一张赛博朋克风格的城市夜景图"
|
|
140
|
+
> "把这段文字转成语音"
|
package/README_EN.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# mmx-mcp-server
|
|
2
|
+
|
|
3
|
+
A unified Model Context Protocol (MCP) server for MiniMax, wrapping the `mmx` CLI. Supports text chat, web search, vision understanding, image generation, speech synthesis, video generation, music generation, and quota queries.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
> [中文 README](README.md)
|
|
8
|
+
|
|
9
|
+
## Quick Start (One-sentence setup)
|
|
10
|
+
|
|
11
|
+
If you are using Claude Code, OpenCode, or any other AI CLI tool that supports MCP, simply paste the following prompt to your assistant:
|
|
12
|
+
|
|
13
|
+
> Please install and configure mmx-mcp-server for me:
|
|
14
|
+
> 1. Globally install mmx-cli (`npm install -g mmx-cli`)
|
|
15
|
+
> 2. Clone https://github.com/zth0828/mmx-mcp-server to any directory and run `npm install && npm run build`
|
|
16
|
+
> 3. Add an `mmx` server entry to my MCP config file (e.g. `~/.claude/settings.json`) pointing to the built artifact `dist/index.js`
|
|
17
|
+
> 4. Ask me for my MiniMax API Key and inject it via `env.MINIMAX_API_KEY`
|
|
18
|
+
> 5. Tell me the result when done
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Manual Installation
|
|
23
|
+
|
|
24
|
+
### 1. Install mmx CLI (if not already installed)
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -g mmx-cli
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Verify installation:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
mmx --version
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Install and build the MCP Server
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/zth0828/mmx-mcp-server.git
|
|
40
|
+
cd mmx-mcp-server
|
|
41
|
+
npm install
|
|
42
|
+
npm run build
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 3. Configure the API Key
|
|
46
|
+
|
|
47
|
+
Add the following to your MCP client settings (e.g. `~/.claude/settings.json` under `mcpServers`):
|
|
48
|
+
|
|
49
|
+
#### Option 1: API Key via Environment Variable (Recommended)
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"mmx": {
|
|
55
|
+
"command": "node",
|
|
56
|
+
"args": ["/path/to/mmx-mcp-server/dist/index.js"],
|
|
57
|
+
"env": {
|
|
58
|
+
"MINIMAX_API_KEY": "sk-xxxxx"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Option 2: API Key via Command-Line Arguments
|
|
66
|
+
|
|
67
|
+
Useful for MCP clients that do not support `env`:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"mmx": {
|
|
73
|
+
"command": "node",
|
|
74
|
+
"args": [
|
|
75
|
+
"/path/to/mmx-mcp-server/dist/index.js",
|
|
76
|
+
"--api-key",
|
|
77
|
+
"sk-xxxxx"
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### Option 3: Fallback to `mmx` CLI Local Auth
|
|
85
|
+
|
|
86
|
+
If no API key is provided via MCP config, the server falls back to the `mmx` CLI's own authentication state (requires prior `mmx auth login --api-key sk-xxxxx`). This is only suitable for local usage.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### Custom Output Directory (Optional)
|
|
91
|
+
|
|
92
|
+
By default, generated images, videos, music, and speech are saved into subdirectories under the **current working directory**:
|
|
93
|
+
|
|
94
|
+
- `mmx_image/` — Images
|
|
95
|
+
- `mmx_video/` — Videos
|
|
96
|
+
- `mmx_music/` — Music
|
|
97
|
+
- `mmx_speech/` — Speech
|
|
98
|
+
|
|
99
|
+
You can change the base output directory with the `MMX_OUTPUT_DIR` environment variable:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"mmx": {
|
|
105
|
+
"command": "node",
|
|
106
|
+
"args": ["/path/to/mmx-mcp-server/dist/index.js"],
|
|
107
|
+
"env": {
|
|
108
|
+
"MINIMAX_API_KEY": "sk-xxxxx",
|
|
109
|
+
"MMX_OUTPUT_DIR": "/Users/xxx/Downloads/mmx-output"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
> Not setting `MMX_OUTPUT_DIR` is perfectly fine — files will simply land under your current project directory.
|
|
117
|
+
|
|
118
|
+
You can also specify an individual output path on each tool call via the `out` parameter.
|
|
119
|
+
|
|
120
|
+
## Available Tools
|
|
121
|
+
|
|
122
|
+
| Tool | Description |
|
|
123
|
+
|------|-------------|
|
|
124
|
+
| `mmx_search` | Web search |
|
|
125
|
+
| `mmx_vision_describe` | Image understanding with custom instructions (e.g. UI critique) |
|
|
126
|
+
| `mmx_text_chat` | Text chat (supports system prompt & JSON mode) |
|
|
127
|
+
| `mmx_image_generate` | Image generation (supports `out` path) |
|
|
128
|
+
| `mmx_speech_synthesize` | Text-to-speech (supports `out` path) |
|
|
129
|
+
| `mmx_video_generate` | Video generation (supports `out` path) |
|
|
130
|
+
| `mmx_music_generate` | Music generation (supports `out` path) |
|
|
131
|
+
| `mmx_quota_show` | Show usage quotas |
|
|
132
|
+
|
|
133
|
+
## Usage Examples
|
|
134
|
+
|
|
135
|
+
Once configured, you can ask your AI assistant directly:
|
|
136
|
+
|
|
137
|
+
> "Search for the latest MiniMax news"
|
|
138
|
+
> "Analyze this UI screenshot and give me improvement suggestions"
|
|
139
|
+
> "Generate a cyberpunk city nightscape image"
|
|
140
|
+
> "Turn this paragraph into speech"
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { spawn, execSync } from "child_process";
|
|
6
|
+
import { mkdir, mkdtemp, readdir } from "fs/promises";
|
|
7
|
+
import { tmpdir } from "os";
|
|
8
|
+
import { join, resolve } from "path";
|
|
9
|
+
const COMMON_ARGS = ["--output", "json", "--non-interactive", "--quiet"];
|
|
10
|
+
const argsIndex = process.argv.indexOf("--api-key");
|
|
11
|
+
const API_KEY = argsIndex !== -1
|
|
12
|
+
? process.argv[argsIndex + 1]
|
|
13
|
+
: process.env.MINIMAX_API_KEY || process.env.MCP_MINIMAX_API_KEY || undefined;
|
|
14
|
+
const API_KEY_ARGS = API_KEY ? ["--api-key", API_KEY] : [];
|
|
15
|
+
const DEFAULT_OUTPUT_BASE = process.env.MMX_OUTPUT_DIR || process.cwd();
|
|
16
|
+
const TOOL_OUTPUT_SUBDIR = {
|
|
17
|
+
mmx_image_generate: "mmx_image",
|
|
18
|
+
mmx_video_generate: "mmx_video",
|
|
19
|
+
mmx_music_generate: "mmx_music",
|
|
20
|
+
mmx_speech_synthesize: "mmx_speech",
|
|
21
|
+
};
|
|
22
|
+
function getDefaultOutputDir(toolName) {
|
|
23
|
+
const subdir = TOOL_OUTPUT_SUBDIR[toolName] || "mmx_output";
|
|
24
|
+
return resolve(DEFAULT_OUTPUT_BASE, subdir);
|
|
25
|
+
}
|
|
26
|
+
async function runMmx(args, cwd) {
|
|
27
|
+
return new Promise((res, rej) => {
|
|
28
|
+
const child = spawn("mmx", [...API_KEY_ARGS, ...COMMON_ARGS, ...args], {
|
|
29
|
+
cwd,
|
|
30
|
+
env: process.env,
|
|
31
|
+
});
|
|
32
|
+
let stdout = "";
|
|
33
|
+
let stderr = "";
|
|
34
|
+
child.stdout.on("data", (data) => {
|
|
35
|
+
stdout += data.toString("utf-8");
|
|
36
|
+
});
|
|
37
|
+
child.stderr.on("data", (data) => {
|
|
38
|
+
stderr += data.toString("utf-8");
|
|
39
|
+
});
|
|
40
|
+
child.on("close", (code) => {
|
|
41
|
+
res({ stdout, stderr, exitCode: code ?? 0 });
|
|
42
|
+
});
|
|
43
|
+
child.on("error", rej);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async function runMmxWithFiles(args, outputDir) {
|
|
47
|
+
const dir = outputDir || (await mkdtemp(join(tmpdir(), "mmx-mcp-")));
|
|
48
|
+
if (outputDir) {
|
|
49
|
+
await mkdir(outputDir, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
const result = await runMmx(args, dir);
|
|
52
|
+
const files = (await readdir(dir)).map((f) => join(dir, f));
|
|
53
|
+
return { ...result, files, tempDir: dir };
|
|
54
|
+
}
|
|
55
|
+
function parseJsonSafe(text) {
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(text);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return text;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const server = new Server({
|
|
64
|
+
name: "mmx-mcp-server",
|
|
65
|
+
version: "1.0.0",
|
|
66
|
+
}, {
|
|
67
|
+
capabilities: {
|
|
68
|
+
tools: {},
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
72
|
+
return {
|
|
73
|
+
tools: [
|
|
74
|
+
{
|
|
75
|
+
name: "mmx_search",
|
|
76
|
+
description: "Search the web using MiniMax. Returns search results with titles, snippets, and URLs. Use this when you need up-to-date information, facts, news, or references from the internet.",
|
|
77
|
+
inputSchema: {
|
|
78
|
+
type: "object",
|
|
79
|
+
properties: {
|
|
80
|
+
query: {
|
|
81
|
+
type: "string",
|
|
82
|
+
description: "Search query. Be specific for better results.",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
required: ["query"],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "mmx_vision_describe",
|
|
90
|
+
description: "Analyze an image with a custom instruction using MiniMax Vision. Useful for: UI/UX critique, extracting text, identifying objects, evaluating designs, or answering specific questions about the image.",
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
image: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "Local file path or URL to the image. Base64 encoding is handled automatically.",
|
|
97
|
+
},
|
|
98
|
+
prompt: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "Instruction or question about the image. Examples: 'Give me UX improvement suggestions for this UI', 'Extract all visible text', 'What is wrong with this layout?', 'Describe the image in detail'. Defaults to 'Describe the image.'",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
required: ["image"],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "mmx_text_chat",
|
|
108
|
+
description: "Chat with MiniMax text models. Supports system prompts and optional JSON mode. Use this for general reasoning, writing, coding help, or structured output.",
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: {
|
|
112
|
+
message: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "The user message to send to the model",
|
|
115
|
+
},
|
|
116
|
+
system: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "Optional system prompt to set the model's behavior or persona",
|
|
119
|
+
},
|
|
120
|
+
json: {
|
|
121
|
+
type: "boolean",
|
|
122
|
+
description: "If true, requests the model to output valid JSON. You should still explicitly ask for JSON in the message or system prompt.",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
required: ["message"],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "mmx_image_generate",
|
|
130
|
+
description: "Generate images with MiniMax. If no output path is provided, images are saved to the default output directory (e.g. ./mmx_image) and their file paths are returned.",
|
|
131
|
+
inputSchema: {
|
|
132
|
+
type: "object",
|
|
133
|
+
properties: {
|
|
134
|
+
prompt: {
|
|
135
|
+
type: "string",
|
|
136
|
+
description: "Detailed description of the image you want to generate",
|
|
137
|
+
},
|
|
138
|
+
aspect_ratio: {
|
|
139
|
+
type: "string",
|
|
140
|
+
description: "Optional aspect ratio. Supported: 16:9, 1:1, 4:3. Default: 1:1",
|
|
141
|
+
},
|
|
142
|
+
n: {
|
|
143
|
+
type: "number",
|
|
144
|
+
description: "Optional number of images to generate. Default: 1",
|
|
145
|
+
},
|
|
146
|
+
out: {
|
|
147
|
+
type: "string",
|
|
148
|
+
description: "Optional custom output file path or directory. If omitted, uses the default directory",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
required: ["prompt"],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: "mmx_speech_synthesize",
|
|
156
|
+
description: "Synthesize speech with MiniMax TTS. Converts text into an audio file. If no output path is provided, the file is saved to the default output directory (e.g. ./mmx_speech).",
|
|
157
|
+
inputSchema: {
|
|
158
|
+
type: "object",
|
|
159
|
+
properties: {
|
|
160
|
+
text: {
|
|
161
|
+
type: "string",
|
|
162
|
+
description: "The text to convert to speech",
|
|
163
|
+
},
|
|
164
|
+
voice: {
|
|
165
|
+
type: "string",
|
|
166
|
+
description: "Optional voice ID. Default depends on the model",
|
|
167
|
+
},
|
|
168
|
+
speed: {
|
|
169
|
+
type: "number",
|
|
170
|
+
description: "Optional speech speed multiplier (e.g. 0.8 - 1.5)",
|
|
171
|
+
},
|
|
172
|
+
out: {
|
|
173
|
+
type: "string",
|
|
174
|
+
description: "Optional output file path. If omitted, a temp file path is returned",
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
required: ["text"],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: "mmx_video_generate",
|
|
182
|
+
description: "Generate video with MiniMax from a text prompt. If no output path is provided, the video is saved to the default output directory (e.g. ./mmx_video) and its file path is returned.",
|
|
183
|
+
inputSchema: {
|
|
184
|
+
type: "object",
|
|
185
|
+
properties: {
|
|
186
|
+
prompt: {
|
|
187
|
+
type: "string",
|
|
188
|
+
description: "Detailed description of the video scene you want to generate",
|
|
189
|
+
},
|
|
190
|
+
duration: {
|
|
191
|
+
type: "number",
|
|
192
|
+
description: "Optional duration in seconds. Check model limits for max duration",
|
|
193
|
+
},
|
|
194
|
+
resolution: {
|
|
195
|
+
type: "string",
|
|
196
|
+
description: "Optional resolution. Example: 768p",
|
|
197
|
+
},
|
|
198
|
+
out: {
|
|
199
|
+
type: "string",
|
|
200
|
+
description: "Optional custom output file path. If omitted, uses the default directory",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
required: ["prompt"],
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: "mmx_music_generate",
|
|
208
|
+
description: "Generate music with MiniMax. Supports instrumental or vocal music. If lyrics and style are provided, generates a song. If no output path is provided, the audio file is saved to the default output directory (e.g. ./mmx_music).",
|
|
209
|
+
inputSchema: {
|
|
210
|
+
type: "object",
|
|
211
|
+
properties: {
|
|
212
|
+
prompt: {
|
|
213
|
+
type: "string",
|
|
214
|
+
description: "Description of the music you want (mood, genre, instruments, scene)",
|
|
215
|
+
},
|
|
216
|
+
lyrics: {
|
|
217
|
+
type: "string",
|
|
218
|
+
description: "Optional lyrics content. If provided, the model will attempt to generate a vocal track",
|
|
219
|
+
},
|
|
220
|
+
style: {
|
|
221
|
+
type: "string",
|
|
222
|
+
description: "Optional music style or genre hint (e.g. 'pop', 'classical piano', 'electronic')",
|
|
223
|
+
},
|
|
224
|
+
out: {
|
|
225
|
+
type: "string",
|
|
226
|
+
description: "Optional output file path. If omitted, a temp file path is returned",
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
required: ["prompt"],
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: "mmx_quota_show",
|
|
234
|
+
description: "Show MiniMax usage quotas and remaining limits for each model or service.",
|
|
235
|
+
inputSchema: {
|
|
236
|
+
type: "object",
|
|
237
|
+
properties: {},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
244
|
+
const { name, arguments: args = {} } = request.params;
|
|
245
|
+
try {
|
|
246
|
+
switch (name) {
|
|
247
|
+
case "mmx_search": {
|
|
248
|
+
const { query } = args;
|
|
249
|
+
const { stdout, stderr, exitCode } = await runMmx([
|
|
250
|
+
"search",
|
|
251
|
+
"query",
|
|
252
|
+
query,
|
|
253
|
+
]);
|
|
254
|
+
return {
|
|
255
|
+
content: [
|
|
256
|
+
{
|
|
257
|
+
type: "text",
|
|
258
|
+
text: exitCode === 0
|
|
259
|
+
? JSON.stringify(parseJsonSafe(stdout), null, 2)
|
|
260
|
+
: stderr || stdout || `Exit code: ${exitCode}`,
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
case "mmx_vision_describe": {
|
|
266
|
+
const { image, prompt } = args;
|
|
267
|
+
const cmdArgs = ["vision", "describe", "--image", image];
|
|
268
|
+
if (prompt)
|
|
269
|
+
cmdArgs.push("--prompt", prompt);
|
|
270
|
+
const { stdout, stderr, exitCode } = await runMmx(cmdArgs);
|
|
271
|
+
return {
|
|
272
|
+
content: [
|
|
273
|
+
{
|
|
274
|
+
type: "text",
|
|
275
|
+
text: exitCode === 0
|
|
276
|
+
? JSON.stringify(parseJsonSafe(stdout), null, 2)
|
|
277
|
+
: stderr || stdout || `Exit code: ${exitCode}`,
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
case "mmx_text_chat": {
|
|
283
|
+
const { message, system, json } = args;
|
|
284
|
+
const cmdArgs = ["text", "chat", "--message", message];
|
|
285
|
+
if (system)
|
|
286
|
+
cmdArgs.push("--system", system);
|
|
287
|
+
if (json)
|
|
288
|
+
cmdArgs.push("--json");
|
|
289
|
+
const { stdout, stderr, exitCode } = await runMmx(cmdArgs);
|
|
290
|
+
return {
|
|
291
|
+
content: [
|
|
292
|
+
{
|
|
293
|
+
type: "text",
|
|
294
|
+
text: exitCode === 0
|
|
295
|
+
? JSON.stringify(parseJsonSafe(stdout), null, 2)
|
|
296
|
+
: stderr || stdout || `Exit code: ${exitCode}`,
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
case "mmx_quota_show": {
|
|
302
|
+
const { stdout, stderr, exitCode } = await runMmx(["quota", "show"]);
|
|
303
|
+
if (exitCode !== 0) {
|
|
304
|
+
return {
|
|
305
|
+
content: [
|
|
306
|
+
{
|
|
307
|
+
type: "text",
|
|
308
|
+
text: stderr || stdout || `Exit code: ${exitCode}`,
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const data = parseJsonSafe(stdout);
|
|
314
|
+
if (typeof data === "string") {
|
|
315
|
+
return {
|
|
316
|
+
content: [{ type: "text", text: data }],
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
const remains = data?.model_remains || [];
|
|
320
|
+
const lines = [];
|
|
321
|
+
lines.push("MiniMax TokenPlan 配额面板");
|
|
322
|
+
lines.push("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
323
|
+
for (const item of remains) {
|
|
324
|
+
const total = item.current_interval_total_count ?? 0;
|
|
325
|
+
// API 的 usage_count 实际是剩余次数(字段名有误)
|
|
326
|
+
const remaining = item.current_interval_usage_count ?? 0;
|
|
327
|
+
const used = total - remaining;
|
|
328
|
+
const name = item.model_name ?? "Unknown";
|
|
329
|
+
lines.push(`${name.padEnd(36)} ${used} / ${total}`);
|
|
330
|
+
}
|
|
331
|
+
lines.push("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
332
|
+
lines.push("注:X / Y 中 X 为已使用次数,Y 为周期总额度");
|
|
333
|
+
return {
|
|
334
|
+
content: [
|
|
335
|
+
{
|
|
336
|
+
type: "text",
|
|
337
|
+
text: lines.join("\n"),
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
case "mmx_image_generate": {
|
|
343
|
+
const { prompt, aspect_ratio, n, out } = args;
|
|
344
|
+
const cmdArgs = ["image", prompt];
|
|
345
|
+
if (aspect_ratio)
|
|
346
|
+
cmdArgs.push("--aspect-ratio", aspect_ratio);
|
|
347
|
+
if (n !== undefined)
|
|
348
|
+
cmdArgs.push("--n", String(n));
|
|
349
|
+
const outputDir = out ? undefined : getDefaultOutputDir("mmx_image_generate");
|
|
350
|
+
const { stdout, stderr, exitCode, files } = await runMmxWithFiles(cmdArgs, outputDir);
|
|
351
|
+
const data = parseJsonSafe(stdout);
|
|
352
|
+
const textParts = [];
|
|
353
|
+
if (exitCode !== 0) {
|
|
354
|
+
textParts.push(stderr || stdout || `Exit code: ${exitCode}`);
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
textParts.push(JSON.stringify(data, null, 2));
|
|
358
|
+
}
|
|
359
|
+
const outputFiles = out ? [resolve(out)] : files;
|
|
360
|
+
if (outputFiles.length > 0) {
|
|
361
|
+
textParts.push("\nGenerated files:");
|
|
362
|
+
outputFiles.forEach((f) => textParts.push(`- ${f}`));
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
content: [
|
|
366
|
+
{
|
|
367
|
+
type: "text",
|
|
368
|
+
text: textParts.join("\n"),
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
case "mmx_speech_synthesize": {
|
|
374
|
+
const { text, voice, speed, out } = args;
|
|
375
|
+
const cmdArgs = ["speech", "synthesize", "--text", text];
|
|
376
|
+
if (voice)
|
|
377
|
+
cmdArgs.push("--voice", voice);
|
|
378
|
+
if (speed !== undefined)
|
|
379
|
+
cmdArgs.push("--speed", String(speed));
|
|
380
|
+
if (out)
|
|
381
|
+
cmdArgs.push("--out", resolve(out));
|
|
382
|
+
const outputDir = out
|
|
383
|
+
? undefined
|
|
384
|
+
: getDefaultOutputDir("mmx_speech_synthesize");
|
|
385
|
+
const { stdout, stderr, exitCode, files } = await runMmxWithFiles(cmdArgs, outputDir);
|
|
386
|
+
const data = parseJsonSafe(stdout);
|
|
387
|
+
const textParts = [];
|
|
388
|
+
if (exitCode !== 0) {
|
|
389
|
+
textParts.push(stderr || stdout || `Exit code: ${exitCode}`);
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
textParts.push(JSON.stringify(data, null, 2));
|
|
393
|
+
}
|
|
394
|
+
const outputFiles = out ? [resolve(out)] : files;
|
|
395
|
+
if (outputFiles.length > 0) {
|
|
396
|
+
textParts.push("\nGenerated files:");
|
|
397
|
+
outputFiles.forEach((f) => textParts.push(`- ${f}`));
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
content: [
|
|
401
|
+
{
|
|
402
|
+
type: "text",
|
|
403
|
+
text: textParts.join("\n"),
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
case "mmx_video_generate": {
|
|
409
|
+
const { prompt, duration, resolution, out } = args;
|
|
410
|
+
const cmdArgs = ["video", "generate", "--prompt", prompt];
|
|
411
|
+
if (duration !== undefined)
|
|
412
|
+
cmdArgs.push("--duration", String(duration));
|
|
413
|
+
if (resolution)
|
|
414
|
+
cmdArgs.push("--resolution", resolution);
|
|
415
|
+
if (out)
|
|
416
|
+
cmdArgs.push("--out", resolve(out));
|
|
417
|
+
const outputDir = out
|
|
418
|
+
? undefined
|
|
419
|
+
: getDefaultOutputDir("mmx_video_generate");
|
|
420
|
+
const { stdout, stderr, exitCode, files } = await runMmxWithFiles(cmdArgs, outputDir);
|
|
421
|
+
const data = parseJsonSafe(stdout);
|
|
422
|
+
const textParts = [];
|
|
423
|
+
if (exitCode !== 0) {
|
|
424
|
+
textParts.push(stderr || stdout || `Exit code: ${exitCode}`);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
textParts.push(JSON.stringify(data, null, 2));
|
|
428
|
+
}
|
|
429
|
+
const outputFiles = out ? [resolve(out)] : files;
|
|
430
|
+
if (outputFiles.length > 0) {
|
|
431
|
+
textParts.push("\nGenerated files:");
|
|
432
|
+
outputFiles.forEach((f) => textParts.push(`- ${f}`));
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
content: [
|
|
436
|
+
{
|
|
437
|
+
type: "text",
|
|
438
|
+
text: textParts.join("\n"),
|
|
439
|
+
},
|
|
440
|
+
],
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
case "mmx_music_generate": {
|
|
444
|
+
const { prompt, lyrics, style, out } = args;
|
|
445
|
+
const cmdArgs = ["music", "generate", "--prompt", prompt];
|
|
446
|
+
if (lyrics)
|
|
447
|
+
cmdArgs.push("--lyrics", lyrics);
|
|
448
|
+
if (style)
|
|
449
|
+
cmdArgs.push("--style", style);
|
|
450
|
+
if (out)
|
|
451
|
+
cmdArgs.push("--out", resolve(out));
|
|
452
|
+
const outputDir = out
|
|
453
|
+
? undefined
|
|
454
|
+
: getDefaultOutputDir("mmx_music_generate");
|
|
455
|
+
const { stdout, stderr, exitCode, files } = await runMmxWithFiles(cmdArgs, outputDir);
|
|
456
|
+
const data = parseJsonSafe(stdout);
|
|
457
|
+
const textParts = [];
|
|
458
|
+
if (exitCode !== 0) {
|
|
459
|
+
textParts.push(stderr || stdout || `Exit code: ${exitCode}`);
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
textParts.push(JSON.stringify(data, null, 2));
|
|
463
|
+
}
|
|
464
|
+
const outputFiles = out ? [resolve(out)] : files;
|
|
465
|
+
if (outputFiles.length > 0) {
|
|
466
|
+
textParts.push("\nGenerated files:");
|
|
467
|
+
outputFiles.forEach((f) => textParts.push(`- ${f}`));
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
content: [
|
|
471
|
+
{
|
|
472
|
+
type: "text",
|
|
473
|
+
text: textParts.join("\n"),
|
|
474
|
+
},
|
|
475
|
+
],
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
default:
|
|
479
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
catch (err) {
|
|
483
|
+
return {
|
|
484
|
+
content: [
|
|
485
|
+
{
|
|
486
|
+
type: "text",
|
|
487
|
+
text: `Error: ${err.message || String(err)}`,
|
|
488
|
+
},
|
|
489
|
+
],
|
|
490
|
+
isError: true,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
function ensureMmxCli() {
|
|
495
|
+
try {
|
|
496
|
+
execSync("mmx --version", { stdio: "ignore" });
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
console.error("Error: mmx CLI not found. Please install it first: npm install -g mmx-cli");
|
|
500
|
+
process.exit(1);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
function handleCliArgs() {
|
|
504
|
+
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
505
|
+
console.log(`mmx-mcp-server v1.0.0
|
|
506
|
+
|
|
507
|
+
A unified MCP server for MiniMax CLI (mmx).
|
|
508
|
+
|
|
509
|
+
Usage:
|
|
510
|
+
node dist/index.js [options]
|
|
511
|
+
|
|
512
|
+
Options:
|
|
513
|
+
--api-key <key> Pass MiniMax API key via command line
|
|
514
|
+
--help, -h Show this help message
|
|
515
|
+
--version Show version number
|
|
516
|
+
|
|
517
|
+
Environment Variables:
|
|
518
|
+
MINIMAX_API_KEY MiniMax API key (recommended)
|
|
519
|
+
MCP_MINIMAX_API_KEY Alternative API key variable
|
|
520
|
+
MMX_OUTPUT_DIR Base directory for generated files
|
|
521
|
+
`);
|
|
522
|
+
process.exit(0);
|
|
523
|
+
}
|
|
524
|
+
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
525
|
+
console.log("1.0.0");
|
|
526
|
+
process.exit(0);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
async function main() {
|
|
530
|
+
handleCliArgs();
|
|
531
|
+
ensureMmxCli();
|
|
532
|
+
const transport = new StdioServerTransport();
|
|
533
|
+
await server.connect(transport);
|
|
534
|
+
}
|
|
535
|
+
main().catch((err) => {
|
|
536
|
+
console.error("Fatal error:", err);
|
|
537
|
+
process.exit(1);
|
|
538
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mseep/mmx-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A unified Model Context Protocol server for MiniMax CLI (mmx)",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mmx-mcp-server": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"README_EN.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"dev": "tsc --watch",
|
|
19
|
+
"prepare": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"minimax",
|
|
24
|
+
"mmx",
|
|
25
|
+
"ai",
|
|
26
|
+
"multimodal",
|
|
27
|
+
"mseep",
|
|
28
|
+
"mcp-server"
|
|
29
|
+
],
|
|
30
|
+
"author": "bingking <zth285130@163.com>",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/zth0828/mmx-mcp-server.git"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/zth0828/mmx-mcp-server/issues"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/zth0828/mmx-mcp-server#readme",
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.0.4"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^20.0.0",
|
|
45
|
+
"typescript": "^5.0.0"
|
|
46
|
+
},
|
|
47
|
+
"publisher": "mseep"
|
|
48
|
+
}
|