#
tokens: 1265/50000 7/7 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .cursor
│   └── rules
│       └── general.mdc
├── .env.sample
├── .gitignore
├── .prettierrc
├── compose.yml
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── api.ts
│   └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.env.sample:
--------------------------------------------------------------------------------

```
SPEAKER_ID=
SPEED_SCALE=1.0
VOICEVOX_API_URL=http://localhost:50021
```

--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------

```
{
  "semi": true,
  "singleQuote": true,
  "trailingComma": "es5",
  "printWidth": 100,
  "tabWidth": 2
} 
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Environment variables
.env
.env.local
.env.*.local

# Build output
dist/
build/
out/

# Coverage directory
coverage/

# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
.DS_Store

# Logs
logs/
*.log

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity 

# Persona
.cursor/rules/persona.mdc
```

--------------------------------------------------------------------------------
/compose.yml:
--------------------------------------------------------------------------------

```yaml
services:
  voicevox_engine:
    image: voicevox/voicevox_engine:cpu-latest
    ports:
      - '50021:50021'
    tty: true
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "dist",
    "rootDir": "src",
    "declaration": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
} 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "voicevox-mcp",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch & node --watch dist/index.js",
    "clean": "rimraf dist"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.11.4",
    "dotenv": "^16.5.0",
    "zod": "^3.24.4"
  },
  "devDependencies": {
    "@types/node": "^22.15.18",
    "prettier": "^3.5.3",
    "typescript": "^5.8.3"
  },
  "description": ""
}

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "fs";
import { exec } from "child_process";
import dotenv from "dotenv";
import { fetchSpeakers, createAudioQuery, synthesizeVoice } from "./api.js";
dotenv.config();


// Create an MCP server
const server = new McpServer({
  name: "voicevox-mcp",
  version: "1.0.0",
});

// ファイル保存用関数
function saveAudioFile(buffer: Buffer, filePath: string) {
  try {
    fs.writeFileSync(filePath, buffer);
    console.log("ファイル保存成功");
  } catch (e) {
    console.error("ファイル保存エラー:", e);
  }
}

// 音声再生用関数
function playAudio(filePath: string) {
  exec(`afplay ${filePath}`, (err) => {
    if (err) {
      console.error("音声再生エラー:", err);
    } else {
      console.log("Audio playback completed");
    }
  });
}

// Add an additional tool
server.tool("speakers",
  {},
  async () => {
    const data = await fetchSpeakers();
    return {
      content: [{ type: "text", text: JSON.stringify(data) }],
    }
  }
)
  
server.tool("speak",
  { text: z.string() },
  async ({ text }) => {
    const resolvedSpeakerId = Number(process.env.SPEAKER_ID);
    if (!resolvedSpeakerId || isNaN(resolvedSpeakerId)) {
      throw new Error("speaker_idが指定されてないか、環境変数SPEAKER_IDが不正です");
    }
    const query = await createAudioQuery(text, resolvedSpeakerId);
    const buffer = await synthesizeVoice(query, resolvedSpeakerId);
    const filePath = "/tmp/voicevox.wav";
    saveAudioFile(buffer, filePath);
    playAudio(filePath);
    return {
      content: [
        {
          type: "text",
          text: "OK",
        }
      ]
    };
  }
)


const transport = new StdioServerTransport();
await server.connect(transport);
```