@justmpm/mmx-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @justmpm/mmx-cli - Barrel export de tools
3
+ *
4
+ * Cada módulo de domínio exporta um array `*Tools: McpTool[]`.
5
+ * Este barrel agrega todos em uma lista flat consumida pelo `index.ts`.
6
+ */
7
+ import type { McpTool } from "../types.js";
8
+ export declare const TOOLS: readonly McpTool[];
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @justmpm/mmx-cli - Barrel export de tools
3
+ *
4
+ * Cada módulo de domínio exporta um array `*Tools: McpTool[]`.
5
+ * Este barrel agrega todos em uma lista flat consumida pelo `index.ts`.
6
+ */
7
+ import { authTools } from "./auth.js";
8
+ import { imageGenerateTool } from "./image-generate.js";
9
+ import { speechSynthesizeTool } from "./speech-synthesize.js";
10
+ import { videoTools } from "./video.js";
11
+ import { llmAuxTools } from "./text-music-vision.js";
12
+ import { fileTools } from "./files.js";
13
+ export const TOOLS = [
14
+ ...authTools,
15
+ imageGenerateTool,
16
+ speechSynthesizeTool,
17
+ ...videoTools,
18
+ ...llmAuxTools,
19
+ ...fileTools,
20
+ ];
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @justmpm/mmx-cli - Tool: mmx_speech_synthesize
3
+ *
4
+ * Sintetiza fala (TTS) usando MiniMax speech-2.8. Salva no outputDir do user
5
+ * e retorna resource_link com file URI.
6
+ *
7
+ * IMPORTANTE: outputDir é OBRIGATÓRIO.
8
+ */
9
+ import { z } from "zod";
10
+ import type { McpTool, ToolResult } from "../types.js";
11
+ export declare const name = "mmx_speech_synthesize";
12
+ export declare const description: string;
13
+ export declare const inputSchema: z.ZodObject<{
14
+ outputDir: z.ZodString;
15
+ text: z.ZodString;
16
+ voice: z.ZodOptional<z.ZodString>;
17
+ model: z.ZodOptional<z.ZodEnum<{
18
+ "speech-2.8-hd": "speech-2.8-hd";
19
+ "speech-2.8-turbo": "speech-2.8-turbo";
20
+ "speech-2.6": "speech-2.6";
21
+ "speech-02": "speech-02";
22
+ }>>;
23
+ speed: z.ZodOptional<z.ZodNumber>;
24
+ volume: z.ZodOptional<z.ZodNumber>;
25
+ pitch: z.ZodOptional<z.ZodNumber>;
26
+ format: z.ZodOptional<z.ZodEnum<{
27
+ mp3: "mp3";
28
+ wav: "wav";
29
+ pcm: "pcm";
30
+ }>>;
31
+ sampleRate: z.ZodOptional<z.ZodNumber>;
32
+ language: z.ZodOptional<z.ZodString>;
33
+ subtitles: z.ZodOptional<z.ZodBoolean>;
34
+ }, z.core.$strip>;
35
+ export declare function handler(args: unknown): Promise<ToolResult>;
36
+ export declare const speechSynthesizeTool: McpTool;
37
+ //# sourceMappingURL=speech-synthesize.d.ts.map
@@ -0,0 +1,137 @@
1
+ /**
2
+ * @justmpm/mmx-cli - Tool: mmx_speech_synthesize
3
+ *
4
+ * Sintetiza fala (TTS) usando MiniMax speech-2.8. Salva no outputDir do user
5
+ * e retorna resource_link com file URI.
6
+ *
7
+ * IMPORTANTE: outputDir é OBRIGATÓRIO.
8
+ */
9
+ import { z } from "zod";
10
+ import { basename, join } from "node:path";
11
+ import { handleToolError, runMmx, validateOutputDir } from "../utils.js";
12
+ import { resourceFromPath, textContent } from "../media.js";
13
+ // =============================================================================
14
+ // Definição da Tool
15
+ // =============================================================================
16
+ export const name = "mmx_speech_synthesize";
17
+ export const description = "Sintetiza **fala** a partir de texto (TTS) usando MiniMax speech-2.8-hd (qualidade) ou speech-2.8-turbo (latência). " +
18
+ "**outputDir é OBRIGATÓRIO** — escolha onde salvar (ex: ~/Audio/mmx). " +
19
+ "Retorna resource_link com .mp3/.wav via file URI. " +
20
+ "Rate limit: 60 RPM. Máx 10.000 caracteres por chamada. " +
21
+ "**Para música** use mmx_music_generate. " +
22
+ "**Voz PT-BR recomendada**: 'VozMatheusKodaPython' (clonada). " +
23
+ "Liste vozes com mmx_speech_voices antes.";
24
+ export const inputSchema = z.object({
25
+ outputDir: z
26
+ .string()
27
+ .min(1)
28
+ .describe("Diretório ABSOLUTO onde salvar o áudio. Obrigatório."),
29
+ text: z
30
+ .string()
31
+ .min(1)
32
+ .max(10_000)
33
+ .describe("Texto a sintetizar. Máx 10.000 caracteres."),
34
+ voice: z
35
+ .string()
36
+ .optional()
37
+ .describe("Voice ID. Default: English_expressive_narrator. PT-BR: 'VozMatheusKodaPython'."),
38
+ model: z
39
+ .enum(["speech-2.8-hd", "speech-2.8-turbo", "speech-2.6", "speech-02"])
40
+ .optional()
41
+ .describe("Modelo. Default: speech-2.8-hd (qualidade). Use turbo pra latência."),
42
+ speed: z.number().optional().describe("Multiplicador de velocidade. Default 1.0."),
43
+ volume: z.number().optional().describe("Nível de volume."),
44
+ pitch: z.number().optional().describe("Ajuste de pitch."),
45
+ format: z
46
+ .enum(["mp3", "wav", "pcm"])
47
+ .optional()
48
+ .describe("Formato do áudio. Default mp3."),
49
+ sampleRate: z.number().int().optional().describe("Sample rate em Hz. Default 32000."),
50
+ language: z.string().optional().describe("Language boost (ex: 'pt-BR')."),
51
+ subtitles: z.boolean().optional().describe("Baixar legendas .srt junto."),
52
+ });
53
+ // =============================================================================
54
+ // Handler
55
+ // =============================================================================
56
+ export async function handler(args) {
57
+ const parsed = inputSchema.safeParse(args);
58
+ if (!parsed.success) {
59
+ return {
60
+ content: [
61
+ {
62
+ type: "text",
63
+ text: `Erro de validação: ${parsed.error.issues
64
+ .map((i) => `${i.path.join(".")}: ${i.message}`)
65
+ .join("; ")}`,
66
+ },
67
+ ],
68
+ isError: true,
69
+ };
70
+ }
71
+ const data = parsed.data;
72
+ const dirCheck = validateOutputDir(data.outputDir);
73
+ if (!dirCheck.ok) {
74
+ return {
75
+ content: [{ type: "text", text: `Erro no outputDir: ${dirCheck.error}` }],
76
+ isError: true,
77
+ };
78
+ }
79
+ const outputDir = dirCheck.resolved;
80
+ const ext = data.format ?? "mp3";
81
+ const outFile = join(outputDir, `mmx-tts-${Date.now()}.${ext}`);
82
+ const cliArgs = [
83
+ "speech",
84
+ "synthesize",
85
+ "--text",
86
+ data.text,
87
+ "--out",
88
+ outFile,
89
+ "--quiet",
90
+ ];
91
+ if (data.voice)
92
+ cliArgs.push("--voice", data.voice);
93
+ if (data.model)
94
+ cliArgs.push("--model", data.model);
95
+ if (data.speed !== undefined)
96
+ cliArgs.push("--speed", String(data.speed));
97
+ if (data.volume !== undefined)
98
+ cliArgs.push("--volume", String(data.volume));
99
+ if (data.pitch !== undefined)
100
+ cliArgs.push("--pitch", String(data.pitch));
101
+ if (data.format)
102
+ cliArgs.push("--format", data.format);
103
+ if (data.sampleRate)
104
+ cliArgs.push("--sample-rate", String(data.sampleRate));
105
+ if (data.language)
106
+ cliArgs.push("--language", data.language);
107
+ if (data.subtitles)
108
+ cliArgs.push("--subtitles");
109
+ const result = await runMmx(cliArgs, { timeoutMs: 120_000 });
110
+ if (result.exitCode !== 0) {
111
+ return handleToolError("sintetizar fala (TTS)", result);
112
+ }
113
+ try {
114
+ return {
115
+ content: [
116
+ resourceFromPath(outFile, undefined, `Áudio TTS (${basename(outFile)})`),
117
+ ],
118
+ };
119
+ }
120
+ catch (err) {
121
+ const msg = err instanceof Error ? err.message : String(err);
122
+ return {
123
+ content: [textContent(`Áudio gerado mas arquivo não encontrado em ${outFile}: ${msg}`)],
124
+ isError: true,
125
+ };
126
+ }
127
+ }
128
+ // =============================================================================
129
+ // Aggregate export
130
+ // =============================================================================
131
+ export const speechSynthesizeTool = {
132
+ name: "mmx_speech_synthesize",
133
+ description,
134
+ inputSchema,
135
+ handler,
136
+ };
137
+ //# sourceMappingURL=speech-synthesize.js.map
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @justmpm/mmx-cli - Tools de texto, música, visão e busca
3
+ *
4
+ * Tools "leves" que retornam text content block (não binários).
5
+ * - mmx_text_chat: LLM MiniMax (M2.7 default)
6
+ * - mmx_music_generate: gera música instrumental ou com letras
7
+ * - mmx_music_cover: cover de áudio existente
8
+ * - mmx_vision_describe: VLM para descrever imagens
9
+ * - mmx_search_query: busca web via MiniMax
10
+ * - mmx_speech_voices: lista vozes TTS disponíveis
11
+ */
12
+ import { z } from "zod";
13
+ import type { McpTool, ToolResult } from "../types.js";
14
+ export declare const textChatName = "mmx_text_chat";
15
+ export declare const textChatDescription: string;
16
+ export declare const textChatInputSchema: z.ZodObject<{
17
+ message: z.ZodString;
18
+ system: z.ZodOptional<z.ZodString>;
19
+ model: z.ZodOptional<z.ZodEnum<{
20
+ "MiniMax-M3": "MiniMax-M3";
21
+ "MiniMax-M2.7": "MiniMax-M2.7";
22
+ "MiniMax-M2.7-highspeed": "MiniMax-M2.7-highspeed";
23
+ "MiniMax-M2.5": "MiniMax-M2.5";
24
+ "MiniMax-M2.5-highspeed": "MiniMax-M2.5-highspeed";
25
+ }>>;
26
+ maxTokens: z.ZodOptional<z.ZodNumber>;
27
+ temperature: z.ZodOptional<z.ZodNumber>;
28
+ }, z.core.$strip>;
29
+ export declare function textChatHandler(args: unknown): Promise<ToolResult>;
30
+ export declare const musicGenerateName = "mmx_music_generate";
31
+ export declare const musicGenerateDescription: string;
32
+ export declare const musicGenerateInputSchema: z.ZodObject<{
33
+ outputDir: z.ZodString;
34
+ prompt: z.ZodOptional<z.ZodString>;
35
+ lyrics: z.ZodOptional<z.ZodString>;
36
+ instrumental: z.ZodOptional<z.ZodBoolean>;
37
+ vocals: z.ZodOptional<z.ZodString>;
38
+ genre: z.ZodOptional<z.ZodString>;
39
+ mood: z.ZodOptional<z.ZodString>;
40
+ instruments: z.ZodOptional<z.ZodString>;
41
+ tempo: z.ZodOptional<z.ZodString>;
42
+ bpm: z.ZodOptional<z.ZodNumber>;
43
+ aigcWatermark: z.ZodOptional<z.ZodBoolean>;
44
+ }, z.core.$strip>;
45
+ export declare function musicGenerateHandler(args: unknown): Promise<ToolResult>;
46
+ export declare const visionDescribeName = "mmx_vision_describe";
47
+ export declare const visionDescribeDescription: string;
48
+ export declare const visionDescribeInputSchema: z.ZodObject<{
49
+ image: z.ZodOptional<z.ZodString>;
50
+ fileId: z.ZodOptional<z.ZodString>;
51
+ prompt: z.ZodOptional<z.ZodString>;
52
+ }, z.core.$strip>;
53
+ export declare function visionDescribeHandler(args: unknown): Promise<ToolResult>;
54
+ export declare const searchQueryName = "mmx_search_query";
55
+ export declare const searchQueryDescription: string;
56
+ export declare const searchQueryInputSchema: z.ZodObject<{
57
+ q: z.ZodString;
58
+ }, z.core.$strip>;
59
+ export declare function searchQueryHandler(args: unknown): Promise<ToolResult>;
60
+ export declare const speechVoicesName = "mmx_speech_voices";
61
+ export declare const speechVoicesDescription: string;
62
+ export declare const speechVoicesInputSchema: z.ZodObject<{}, z.core.$strip>;
63
+ export declare function speechVoicesHandler(_args: unknown): Promise<ToolResult>;
64
+ export declare const llmAuxTools: McpTool[];
65
+ //# sourceMappingURL=text-music-vision.d.ts.map
@@ -0,0 +1,312 @@
1
+ /**
2
+ * @justmpm/mmx-cli - Tools de texto, música, visão e busca
3
+ *
4
+ * Tools "leves" que retornam text content block (não binários).
5
+ * - mmx_text_chat: LLM MiniMax (M2.7 default)
6
+ * - mmx_music_generate: gera música instrumental ou com letras
7
+ * - mmx_music_cover: cover de áudio existente
8
+ * - mmx_vision_describe: VLM para descrever imagens
9
+ * - mmx_search_query: busca web via MiniMax
10
+ * - mmx_speech_voices: lista vozes TTS disponíveis
11
+ */
12
+ import { z } from "zod";
13
+ import { basename, join } from "node:path";
14
+ import { handleToolError, runMmx, validateInputFile, validateOutputDir } from "../utils.js";
15
+ import { resourceFromPath, textContent } from "../media.js";
16
+ // =============================================================================
17
+ // mmx_text_chat
18
+ // =============================================================================
19
+ export const textChatName = "mmx_text_chat";
20
+ export const textChatDescription = "Chat completion usando MiniMax M-series LLM (default MiniMax-M2.7). " +
21
+ "Rate limit: 200 RPM (M3) ou 500 RPM (M2.x). " +
22
+ "Use quando quiser respostas LLM da plataforma MiniMax em vez da LLM que está orquestrando este MCP.";
23
+ export const textChatInputSchema = z.object({
24
+ message: z
25
+ .string()
26
+ .min(1)
27
+ .describe("Mensagem do usuário. Para system: prefixe com 'system:'."),
28
+ system: z.string().optional().describe("System prompt."),
29
+ model: z
30
+ .enum([
31
+ "MiniMax-M3",
32
+ "MiniMax-M2.7",
33
+ "MiniMax-M2.7-highspeed",
34
+ "MiniMax-M2.5",
35
+ "MiniMax-M2.5-highspeed",
36
+ ])
37
+ .optional()
38
+ .describe("Modelo. Default: MiniMax-M2.7."),
39
+ maxTokens: z.number().int().positive().optional().describe("Máx tokens. Default 4096."),
40
+ temperature: z
41
+ .number()
42
+ .min(0.0)
43
+ .max(1.0)
44
+ .optional()
45
+ .describe("Sampling temperature. (0.0, 1.0]."),
46
+ });
47
+ export async function textChatHandler(args) {
48
+ const parsed = textChatInputSchema.safeParse(args);
49
+ if (!parsed.success) {
50
+ return {
51
+ content: [
52
+ {
53
+ type: "text",
54
+ text: `Erro de validação: ${parsed.error.issues
55
+ .map((i) => `${i.path.join(".")}: ${i.message}`)
56
+ .join("; ")}`,
57
+ },
58
+ ],
59
+ isError: true,
60
+ };
61
+ }
62
+ const data = parsed.data;
63
+ const cliArgs = ["text", "chat", "--message", data.message, "--non-interactive"];
64
+ if (data.system)
65
+ cliArgs.push("--system", data.system);
66
+ if (data.model)
67
+ cliArgs.push("--model", data.model);
68
+ if (data.maxTokens)
69
+ cliArgs.push("--max-tokens", String(data.maxTokens));
70
+ if (data.temperature)
71
+ cliArgs.push("--temperature", String(data.temperature));
72
+ const result = await runMmx(cliArgs, { timeoutMs: 120_000 });
73
+ if (result.exitCode !== 0) {
74
+ return handleToolError("rodar chat completion", result);
75
+ }
76
+ return { content: [{ type: "text", text: result.stdout }] };
77
+ }
78
+ // =============================================================================
79
+ // mmx_music_generate
80
+ // =============================================================================
81
+ export const musicGenerateName = "mmx_music_generate";
82
+ export const musicGenerateDescription = "Gera **música** (instrumental ou cantada) usando MiniMax music-2.6. " +
83
+ "**outputDir é OBRIGATÓRIO** — escolha onde salvar (ex: ~/Music/mmx). " +
84
+ "Retorna resource_link com .mp3 via file URI. " +
85
+ "Rate limit: 120 RPM (max 20 conexões simultâneas). " +
86
+ "**Para fala/narração** use mmx_speech_synthesize. " +
87
+ "**Pelo menos um** destes deve ser fornecido: prompt (estilo), lyrics (letra) ou instrumental=true. " +
88
+ "Parâmetros de controle fino (genre, mood, tempo, bpm) melhoram a qualidade mas são opcionais.";
89
+ export const musicGenerateInputSchema = z.object({
90
+ outputDir: z
91
+ .string()
92
+ .min(1)
93
+ .describe("Diretório ABSOLUTO onde salvar a música. Obrigatório."),
94
+ prompt: z.string().optional().describe("Descrição do estilo musical. Ex: 'Upbeat folk', 'Cinematic orchestral'."),
95
+ lyrics: z.string().optional().describe("Letra com tags de estrutura (verse, chorus, bridge)."),
96
+ instrumental: z
97
+ .boolean()
98
+ .optional()
99
+ .describe("Gerar música sem vocal. Não pode combinar com lyrics."),
100
+ vocals: z.string().optional().describe("Estilo vocal. Ex: 'warm male baritone', 'bright female soprano'."),
101
+ genre: z.string().optional().describe("Gênero musical. Ex: 'pop', 'rock', 'jazz', 'classical', 'electronic'."),
102
+ mood: z.string().optional().describe("Humor da música. Ex: 'happy', 'melancholic', 'energetic', 'calm'."),
103
+ instruments: z.string().optional().describe("Instrumentos separados por vírgula. Ex: 'acoustic guitar, piano, drums'."),
104
+ tempo: z.string().optional().describe("Descrição do tempo. Ex: 'slow', 'moderate', 'fast'."),
105
+ bpm: z.number().int().positive().optional().describe("BPM exato (40-220). Ex: 120."),
106
+ aigcWatermark: z.boolean().optional(),
107
+ });
108
+ export async function musicGenerateHandler(args) {
109
+ const parsed = musicGenerateInputSchema.safeParse(args);
110
+ if (!parsed.success) {
111
+ return {
112
+ content: [{ type: "text", text: `Erro de validação: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}` }],
113
+ isError: true,
114
+ };
115
+ }
116
+ const data = parsed.data;
117
+ if (!data.prompt && !data.lyrics && !data.instrumental) {
118
+ return {
119
+ content: [{ type: "text", text: "Informe pelo menos um: prompt, lyrics ou instrumental=true." }],
120
+ isError: true,
121
+ };
122
+ }
123
+ const dirCheck = validateOutputDir(data.outputDir);
124
+ if (!dirCheck.ok) {
125
+ return {
126
+ content: [{ type: "text", text: `Erro no outputDir: ${dirCheck.error}` }],
127
+ isError: true,
128
+ };
129
+ }
130
+ const outFile = join(dirCheck.resolved, `mmx-music-${Date.now()}.mp3`);
131
+ const cliArgs = ["music", "generate", "--out", outFile, "--quiet"];
132
+ if (data.prompt)
133
+ cliArgs.push("--prompt", data.prompt);
134
+ if (data.lyrics)
135
+ cliArgs.push("--lyrics", data.lyrics);
136
+ if (data.instrumental)
137
+ cliArgs.push("--instrumental");
138
+ if (data.vocals)
139
+ cliArgs.push("--vocals", data.vocals);
140
+ if (data.genre)
141
+ cliArgs.push("--genre", data.genre);
142
+ if (data.mood)
143
+ cliArgs.push("--mood", data.mood);
144
+ if (data.instruments)
145
+ cliArgs.push("--instruments", data.instruments);
146
+ if (data.tempo)
147
+ cliArgs.push("--tempo", data.tempo);
148
+ if (data.bpm)
149
+ cliArgs.push("--bpm", String(data.bpm));
150
+ if (data.aigcWatermark)
151
+ cliArgs.push("--aigc-watermark");
152
+ const result = await runMmx(cliArgs, { timeoutMs: 180_000 });
153
+ if (result.exitCode !== 0) {
154
+ return handleToolError("gerar música", result);
155
+ }
156
+ try {
157
+ return {
158
+ content: [
159
+ resourceFromPath(outFile, "audio/mpeg", `Música gerada (${basename(outFile)})`),
160
+ ],
161
+ };
162
+ }
163
+ catch (err) {
164
+ const msg = err instanceof Error ? err.message : String(err);
165
+ return {
166
+ content: [textContent(`Música gerada mas arquivo não encontrado em ${outFile}: ${msg}`)],
167
+ isError: true,
168
+ };
169
+ }
170
+ }
171
+ // =============================================================================
172
+ // mmx_vision_describe
173
+ // =============================================================================
174
+ export const visionDescribeName = "mmx_vision_describe";
175
+ export const visionDescribeDescription = "Analisa uma imagem usando VLM da MiniMax. Retorna text content block com descrição. " +
176
+ "**Para gerar imagem a partir de prompt** use mmx_image_generate (esta tool ANALISA, não gera). " +
177
+ "Para imagens >1MB, faça upload com mmx_file_upload primeiro e use o fileId retornado (evita base64). " +
178
+ "Combine com mmx_file_list pra descobrir fileIds disponíveis.";
179
+ export const visionDescribeInputSchema = z.object({
180
+ image: z.string().optional().describe("Path local (absoluto recomendado) ou URL da imagem."),
181
+ fileId: z
182
+ .string()
183
+ .optional()
184
+ .describe("ID de arquivo pré-uploaded (via mmx_file_upload). Pula base64 — melhor para imagens >1MB."),
185
+ prompt: z
186
+ .string()
187
+ .optional()
188
+ .describe("Pergunta sobre a imagem. Default: 'Describe the image.'"),
189
+ });
190
+ export async function visionDescribeHandler(args) {
191
+ const parsed = visionDescribeInputSchema.safeParse(args);
192
+ if (!parsed.success) {
193
+ return {
194
+ content: [{ type: "text", text: `Erro de validação: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}` }],
195
+ isError: true,
196
+ };
197
+ }
198
+ const data = parsed.data;
199
+ if (!data.image && !data.fileId) {
200
+ return {
201
+ content: [
202
+ {
203
+ type: "text",
204
+ text: "Erro: forneça `image` (path/URL) OU `fileId`. " +
205
+ "Para imagens >1MB, faça upload com mmx_file_upload primeiro e use o fileId retornado.",
206
+ },
207
+ ],
208
+ isError: true,
209
+ };
210
+ }
211
+ const cliArgs = ["vision", "describe", "--non-interactive"];
212
+ if (data.image) {
213
+ // Validação de segurança: rejeita path traversal, device files, e arquivos >10MB
214
+ const fileCheck = validateInputFile(data.image);
215
+ if (!fileCheck.ok) {
216
+ return {
217
+ content: [{ type: "text", text: `Erro de validação do path: ${fileCheck.error}` }],
218
+ isError: true,
219
+ };
220
+ }
221
+ cliArgs.push("--image", fileCheck.resolved);
222
+ }
223
+ if (data.fileId)
224
+ cliArgs.push("--file-id", data.fileId);
225
+ if (data.prompt)
226
+ cliArgs.push("--prompt", data.prompt);
227
+ const result = await runMmx(cliArgs, { timeoutMs: 60_000 });
228
+ if (result.exitCode !== 0) {
229
+ return handleToolError("descrever imagem", result, { filePath: data.image });
230
+ }
231
+ return { content: [{ type: "text", text: result.stdout }] };
232
+ }
233
+ // =============================================================================
234
+ // mmx_search_query
235
+ // =============================================================================
236
+ export const searchQueryName = "mmx_search_query";
237
+ export const searchQueryDescription = "Busca web via MiniMax. Retorna text content block com resultados. " +
238
+ "Rate limit moderado (~60 RPM). " +
239
+ "**Combine com mmx_text_chat**: passe os resultados da busca como contexto para a LLM MiniMax sintetizar/resumir. " +
240
+ "Para queries em outros idiomas, especifique na query (ex: 'MiniMax tutorial em português').";
241
+ export const searchQueryInputSchema = z.object({
242
+ q: z.string().min(1).describe("Query de busca. Seja específico — queries longas retornam resultados melhores."),
243
+ });
244
+ export async function searchQueryHandler(args) {
245
+ const parsed = searchQueryInputSchema.safeParse(args);
246
+ if (!parsed.success) {
247
+ return {
248
+ content: [{ type: "text", text: `Erro de validação: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}` }],
249
+ isError: true,
250
+ };
251
+ }
252
+ const result = await runMmx(["search", "query", "--q", parsed.data.q, "--quiet"]);
253
+ if (result.exitCode !== 0) {
254
+ return handleToolError("buscar na web", result);
255
+ }
256
+ return { content: [{ type: "text", text: result.stdout }] };
257
+ }
258
+ // =============================================================================
259
+ // mmx_speech_voices
260
+ // =============================================================================
261
+ export const speechVoicesName = "mmx_speech_voices";
262
+ export const speechVoicesDescription = "Lista todas as vozes TTS disponíveis na MiniMax (retorna JSON). " +
263
+ "**Use antes de mmx_speech_synthesize** para escolher o voice_id correto. " +
264
+ "Vozes padrão (English_expressive_narrator etc) sempre disponíveis. " +
265
+ "Vozes clonadas aparecem apenas após mmx_file_upload.";
266
+ export const speechVoicesInputSchema = z.object({});
267
+ export async function speechVoicesHandler(_args) {
268
+ const result = await runMmx(["speech", "voices", "--output", "json"]);
269
+ if (result.exitCode !== 0) {
270
+ return handleToolError("listar vozes TTS", result);
271
+ }
272
+ return { content: [{ type: "text", text: result.stdout }] };
273
+ }
274
+ // =============================================================================
275
+ // Aggregate export
276
+ // =============================================================================
277
+ export const llmAuxTools = [
278
+ {
279
+ name: "mmx_text_chat",
280
+ description: textChatDescription,
281
+ inputSchema: textChatInputSchema,
282
+ handler: textChatHandler,
283
+ },
284
+ {
285
+ name: "mmx_music_generate",
286
+ description: musicGenerateDescription,
287
+ inputSchema: musicGenerateInputSchema,
288
+ handler: musicGenerateHandler,
289
+ },
290
+ // TODO(2026-Q3, Koda): adicionar mmx_music_cover (input: audio path/url + prompt).
291
+ // Bloqueado: depende de design de upload para audio de referência (>10MB),
292
+ // que requer decisão sobre storage temporário vs outputDir explícito.
293
+ {
294
+ name: "mmx_vision_describe",
295
+ description: visionDescribeDescription,
296
+ inputSchema: visionDescribeInputSchema,
297
+ handler: visionDescribeHandler,
298
+ },
299
+ {
300
+ name: "mmx_search_query",
301
+ description: searchQueryDescription,
302
+ inputSchema: searchQueryInputSchema,
303
+ handler: searchQueryHandler,
304
+ },
305
+ {
306
+ name: "mmx_speech_voices",
307
+ description: speechVoicesDescription,
308
+ inputSchema: speechVoicesInputSchema,
309
+ handler: speechVoicesHandler,
310
+ },
311
+ ];
312
+ //# sourceMappingURL=text-music-vision.js.map
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @justmpm/mmx-cli - Tool: mmx_video_generate (ASYNC)
3
+ *
4
+ * Gera vídeo via MiniMax Hailuo 2.3 com polling interno.
5
+ * Salva no outputDir do user. Retorna resource_link (.mp4 via file URI).
6
+ *
7
+ * Notes (notebook MiniMax):
8
+ * - Video is strictly async. API returns task_id; poll every 10s.
9
+ * - file_id expires after 9h. We download immediately on Success.
10
+ * - Rate limit: 5 RPM.
11
+ * - Costs: $0.19-$0.49 per 6s video depending on resolution and model.
12
+ * - Polling aborta se o cliente MCP desconectar (AbortController).
13
+ */
14
+ import { z } from "zod";
15
+ import type { McpTool, ToolResult } from "../types.js";
16
+ export declare const name = "mmx_video_generate";
17
+ export declare const description: string;
18
+ export declare const inputSchema: z.ZodObject<{
19
+ outputDir: z.ZodString;
20
+ prompt: z.ZodString;
21
+ model: z.ZodOptional<z.ZodEnum<{
22
+ "MiniMax-Hailuo-2.3": "MiniMax-Hailuo-2.3";
23
+ "MiniMax-Hailuo-2.3-Fast": "MiniMax-Hailuo-2.3-Fast";
24
+ "MiniMax-Hailuo-02": "MiniMax-Hailuo-02";
25
+ }>>;
26
+ firstFrame: z.ZodOptional<z.ZodString>;
27
+ callbackUrl: z.ZodOptional<z.ZodString>;
28
+ maxWaitMs: z.ZodOptional<z.ZodNumber>;
29
+ pollIntervalMs: z.ZodOptional<z.ZodNumber>;
30
+ }, z.core.$strip>;
31
+ export declare function handler(args: unknown): Promise<ToolResult>;
32
+ export declare const taskGetName = "mmx_video_task_get";
33
+ export declare const taskGetDescription: string;
34
+ export declare const taskGetInputSchema: z.ZodObject<{
35
+ taskId: z.ZodString;
36
+ }, z.core.$strip>;
37
+ export declare function taskGetHandler(args: unknown): Promise<ToolResult>;
38
+ export declare const videoTools: McpTool[];
39
+ //# sourceMappingURL=video.d.ts.map