Files
backup_openclaw/projects/stt-mcp/stt_server.mjs
2026-03-30 19:30:25 +09:00

103 lines
3.1 KiB
JavaScript

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "fs";
import path from "path";
import http from "http";
const STT_URL = process.env.STT_URL || "http://llama:9090";
const server = new McpServer({ name: "stt-mcp", version: "1.0.0" });
server.tool(
"stt_transcribe",
"Transcribe an audio file using Whisper STT",
{
file_path: z.string().describe("Absolute path to the audio file"),
},
async ({ file_path }) => {
if (!fs.existsSync(file_path)) {
return { content: [{ type: "text", text: `Error: file not found: ${file_path}` }] };
}
const boundary = "----STTBOUNDARY" + Date.now();
const filename = path.basename(file_path);
const fileBuf = fs.readFileSync(file_path);
// Determine content type from extension
const ext = path.extname(filename).toLowerCase();
const mimeMap = {
".ogg": "audio/ogg",
".mp3": "audio/mpeg",
".wav": "audio/wav",
".m4a": "audio/mp4",
".webm": "audio/webm",
".flac": "audio/flac",
};
const mime = mimeMap[ext] || "application/octet-stream";
// Build multipart body manually (no external dependency)
const parts = [];
parts.push(`--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${filename}"\r\nContent-Type: ${mime}\r\n\r\n`);
parts.push(fileBuf);
parts.push(`\r\n--${boundary}--\r\n`);
const body = Buffer.concat(
parts.map((p) => (typeof p === "string" ? Buffer.from(p) : p))
);
const url = new URL(`${STT_URL}/api/stt`);
return new Promise((resolve, reject) => {
const req = http.request(
{
hostname: url.hostname,
port: url.port,
path: url.pathname,
method: "POST",
headers: {
"Content-Type": `multipart/form-data; boundary=${boundary}`,
"Content-Length": body.length,
},
timeout: 60000,
},
(res) => {
let data = "";
res.on("data", (chunk) => (data += chunk));
res.on("end", () => {
try {
const result = JSON.parse(data);
if (result.ok) {
resolve({
content: [
{
type: "text",
text: `**STT Result** (${result.transcribe_sec}s, ${result.duration_sec}s audio, ${result.language})\n\n${result.text}`,
},
],
});
} else {
resolve({
content: [{ type: "text", text: `STT error: ${data}` }],
});
}
} catch (e) {
resolve({ content: [{ type: "text", text: `Parse error: ${data}` }] });
}
});
}
);
req.on("error", (e) =>
resolve({ content: [{ type: "text", text: `Connection error: ${e.message}` }] })
);
req.write(body);
req.end();
});
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("[stt-mcp] server started");