# Directory Structure ``` ├── .gitignore ├── dist │ └── index.js ├── package-lock.json ├── package.json ├── README.md ├── sounds │ └── completion.mp3 ├── src │ └── index.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # Database 9 | *.db 10 | 11 | # testing 12 | /coverage 13 | 14 | # storybook 15 | storybook-static 16 | *storybook.log 17 | 18 | # playwright 19 | /test-results/ 20 | /playwright-report/ 21 | /playwright/.cache/ 22 | 23 | # next.js 24 | /.next 25 | /out 26 | 27 | # cache 28 | .swc/ 29 | 30 | # production 31 | /build 32 | 33 | # misc 34 | .DS_Store 35 | *.pem 36 | Thumbs.db 37 | 38 | # debug 39 | npm-debug.log* 40 | pnpm-debug.log* 41 | yarn-debug.log* 42 | yarn-error.log* 43 | 44 | # local env files 45 | .env*.local 46 | 47 | # Sentry Config File 48 | .env.sentry-build-plugin 49 | 50 | # local folder 51 | local 52 | 53 | # vercel 54 | .vercel ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Cursor Sound MCP 2 | 3 | A Model Context Protocol (MCP) implementation that plays sound effects after Cursor AI completes code generation. This MCP integrates with Cursor to provide audio feedback for a more interactive coding experience. 4 | 5 | Inspired by @EricListin on X.com - I have made some changes to the code to not error out and be online. 6 | 7 | ## Features 8 | 9 | - Plays a sound effect when Cursor completes code generation 10 | - Uses the Model Context Protocol (MCP) for standardized integration 11 | - Configurable sound effects 12 | - Improved error handling and logging 13 | - Stable JSON response format 14 | 15 | ## Cursor Rules 16 | 17 | Add this to your Cursor custom instructions: 18 | 19 | "EVERY TIME you finish any task or you need something from me, run the sound-mcp MCP server to get my attention." 20 | 21 | ## Installation 22 | 23 | 1. Install dependencies: 24 | ```bash 25 | npm install 26 | ``` 27 | 28 | 2. Add your sound effects: 29 | Place your sound files in the `sounds` directory. The default expected sound is: 30 | - `sounds/completion.mp3` - Played after code generation 31 | 32 | You can find free sound effects on freesound.org. 33 | 34 | 3. Build the project: 35 | ```bash 36 | npm run build 37 | ``` 38 | 39 | ## Usage 40 | 41 | Run the MCP server: 42 | ```bash 43 | npm start 44 | ``` 45 | 46 | The server will start and listen for events from Cursor through the stdio transport. 47 | 48 | ## Configuration 49 | 50 | The sound effects and volume can be customized by: 51 | 1. Replacing the sound files in the `sounds` directory 52 | 2. Modifying the sound file paths in `src/index.ts` 53 | 54 | ## Development 55 | 56 | For development with auto-recompilation: 57 | ```bash 58 | npm run dev 59 | ``` 60 | ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "ES2022", 5 | "moduleResolution": "node", 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "typeRoots": ["./src/types", "./node_modules/@types"] 13 | }, 14 | "include": ["src/**/*"], 15 | "exclude": ["node_modules", "dist"] 16 | } 17 | ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "cursor-sound-mcp", 3 | "version": "1.0.0", 4 | "description": "MCP for Cursor that plays sounds after code generation", 5 | "main": "dist/index.js", 6 | "type": "module", 7 | "scripts": { 8 | "build": "tsc", 9 | "start": "node dist/index.js", 10 | "dev": "tsc -w" 11 | }, 12 | "keywords": [ 13 | "mcp", 14 | "cursor", 15 | "sound" 16 | ], 17 | "author": "", 18 | "license": "ISC", 19 | "dependencies": { 20 | "@modelcontextprotocol/sdk": "latest", 21 | "@types/play-sound": "^1.1.2", 22 | "play-sound": "^1.1.5", 23 | "zod": "^3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@types/node": "^20.0.0", 27 | "typescript": "^5.0.0" 28 | } 29 | } 30 | ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; 2 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 3 | import player from 'play-sound'; 4 | import path from 'path'; 5 | import { fileURLToPath } from 'url'; 6 | 7 | // Get the directory name of the current module 8 | const __filename = fileURLToPath(import.meta.url); 9 | const __dirname = path.dirname(__filename); 10 | 11 | const audioPlayer = player(); 12 | // Use the custom sound file from the sounds directory 13 | const COMPLETION_SOUND = path.join(__dirname, '..', 'sounds', 'completion.mp3'); 14 | 15 | // Set up a proper logging function that uses stderr 16 | const log = (message: string) => { 17 | process.stderr.write(`${message}\n`); 18 | }; 19 | 20 | async function main() { 21 | const server = new McpServer({ 22 | name: 'CursorSoundMCP', 23 | version: '1.0.0' 24 | }); 25 | 26 | // Tool to play sound after code generation 27 | server.tool( 28 | 'playCompletionSound', 29 | 'Plays a completion sound when called', 30 | async () => { 31 | try { 32 | // Play custom sound using a Promise to handle the callback properly 33 | await new Promise<void>((resolve, reject) => { 34 | audioPlayer.play(COMPLETION_SOUND, (err: Error | null) => { 35 | if (err) { 36 | log(`Error playing sound: ${err.message}`); 37 | reject(err); 38 | } else { 39 | log('Played completion sound successfully'); 40 | resolve(); 41 | } 42 | }); 43 | }); 44 | 45 | // Return proper JSON response 46 | return { 47 | content: [{ type: 'text', text: 'Played completion sound' }] 48 | }; 49 | } catch (error) { 50 | log(`Failed to play sound: ${error}`); 51 | return { 52 | content: [{ type: 'text', text: 'Failed to play sound' }] 53 | }; 54 | } 55 | } 56 | ); 57 | 58 | // Start the server using stdio transport 59 | const transport = new StdioServerTransport(); 60 | await server.connect(transport); 61 | 62 | log('Cursor Sound MCP server started...'); 63 | } 64 | 65 | main().catch(error => { 66 | log(`Failed to start MCP server: ${error}`); 67 | process.exit(1); 68 | }); 69 | ```