#
tokens: 1687/50000 7/7 files
lines: on (toggle) GitHub
raw markdown copy reset
# 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:
--------------------------------------------------------------------------------

```
1 | SPEAKER_ID=
2 | SPEED_SCALE=1.0
3 | VOICEVOX_API_URL=http://localhost:50021
```

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

```
1 | {
2 |   "semi": true,
3 |   "singleQuote": true,
4 |   "trailingComma": "es5",
5 |   "printWidth": 100,
6 |   "tabWidth": 2
7 | } 
```

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

```
 1 | # Dependencies
 2 | node_modules/
 3 | npm-debug.log*
 4 | yarn-debug.log*
 5 | yarn-error.log*
 6 | 
 7 | # Environment variables
 8 | .env
 9 | .env.local
10 | .env.*.local
11 | 
12 | # Build output
13 | dist/
14 | build/
15 | out/
16 | 
17 | # Coverage directory
18 | coverage/
19 | 
20 | # IDE and editor files
21 | .idea/
22 | .vscode/
23 | *.swp
24 | *.swo
25 | .DS_Store
26 | 
27 | # Logs
28 | logs/
29 | *.log
30 | 
31 | # Optional npm cache directory
32 | .npm
33 | 
34 | # Optional eslint cache
35 | .eslintcache
36 | 
37 | # Optional REPL history
38 | .node_repl_history
39 | 
40 | # Output of 'npm pack'
41 | *.tgz
42 | 
43 | # Yarn Integrity file
44 | .yarn-integrity 
45 | 
46 | # Persona
47 | .cursor/rules/persona.mdc
```

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

```yaml
1 | services:
2 |   voicevox_engine:
3 |     image: voicevox/voicevox_engine:cpu-latest
4 |     ports:
5 |       - '50021:50021'
6 |     tty: true
```

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

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "NodeNext",
 5 |     "moduleResolution": "NodeNext",
 6 |     "esModuleInterop": true,
 7 |     "strict": true,
 8 |     "skipLibCheck": true,
 9 |     "forceConsistentCasingInFileNames": true,
10 |     "outDir": "dist",
11 |     "rootDir": "src",
12 |     "declaration": true,
13 |     "sourceMap": true
14 |   },
15 |   "include": ["src/**/*"],
16 |   "exclude": ["node_modules", "dist"]
17 | } 
```

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

```json
 1 | {
 2 |   "name": "voicevox-mcp",
 3 |   "version": "1.0.0",
 4 |   "main": "dist/index.js",
 5 |   "types": "dist/index.d.ts",
 6 |   "type": "module",
 7 |   "scripts": {
 8 |     "build": "tsc",
 9 |     "dev": "tsc --watch & node --watch dist/index.js",
10 |     "clean": "rimraf dist"
11 |   },
12 |   "keywords": [],
13 |   "author": "",
14 |   "license": "ISC",
15 |   "dependencies": {
16 |     "@modelcontextprotocol/sdk": "^1.11.4",
17 |     "dotenv": "^16.5.0",
18 |     "zod": "^3.24.4"
19 |   },
20 |   "devDependencies": {
21 |     "@types/node": "^22.15.18",
22 |     "prettier": "^3.5.3",
23 |     "typescript": "^5.8.3"
24 |   },
25 |   "description": ""
26 | }
27 | 
```

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

```typescript
 1 | import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 3 | import { z } from "zod";
 4 | import fs from "fs";
 5 | import { exec } from "child_process";
 6 | import dotenv from "dotenv";
 7 | import { fetchSpeakers, createAudioQuery, synthesizeVoice } from "./api.js";
 8 | dotenv.config();
 9 | 
10 | 
11 | // Create an MCP server
12 | const server = new McpServer({
13 |   name: "voicevox-mcp",
14 |   version: "1.0.0",
15 | });
16 | 
17 | // ファイル保存用関数
18 | function saveAudioFile(buffer: Buffer, filePath: string) {
19 |   try {
20 |     fs.writeFileSync(filePath, buffer);
21 |     console.log("ファイル保存成功");
22 |   } catch (e) {
23 |     console.error("ファイル保存エラー:", e);
24 |   }
25 | }
26 | 
27 | // 音声再生用関数
28 | function playAudio(filePath: string) {
29 |   exec(`afplay ${filePath}`, (err) => {
30 |     if (err) {
31 |       console.error("音声再生エラー:", err);
32 |     } else {
33 |       console.log("Audio playback completed");
34 |     }
35 |   });
36 | }
37 | 
38 | // Add an additional tool
39 | server.tool("speakers",
40 |   {},
41 |   async () => {
42 |     const data = await fetchSpeakers();
43 |     return {
44 |       content: [{ type: "text", text: JSON.stringify(data) }],
45 |     }
46 |   }
47 | )
48 |   
49 | server.tool("speak",
50 |   { text: z.string() },
51 |   async ({ text }) => {
52 |     const resolvedSpeakerId = Number(process.env.SPEAKER_ID);
53 |     if (!resolvedSpeakerId || isNaN(resolvedSpeakerId)) {
54 |       throw new Error("speaker_idが指定されてないか、環境変数SPEAKER_IDが不正です");
55 |     }
56 |     const query = await createAudioQuery(text, resolvedSpeakerId);
57 |     const buffer = await synthesizeVoice(query, resolvedSpeakerId);
58 |     const filePath = "/tmp/voicevox.wav";
59 |     saveAudioFile(buffer, filePath);
60 |     playAudio(filePath);
61 |     return {
62 |       content: [
63 |         {
64 |           type: "text",
65 |           text: "OK",
66 |         }
67 |       ]
68 |     };
69 |   }
70 | )
71 | 
72 | 
73 | const transport = new StdioServerTransport();
74 | await server.connect(transport);
```