@sylweriusz/mcp-ai-voice
Version:
Enable AI agents to express themselves with natural voice synthesis. Zero-configuration multilingual speech that enhances AI interactions through asynchronous voice capabilities.
198 lines ⢠8.43 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
const child_process_1 = require("child_process");
const os = __importStar(require("os"));
const voice_intelligence_js_1 = require("./voice-intelligence.js");
class NexusVoice {
constructor() {
this.initialized = false;
this.voiceIntelligence = new voice_intelligence_js_1.VoiceIntelligence();
}
static getInstance() {
if (!NexusVoice.instance) {
NexusVoice.instance = new NexusVoice();
}
return NexusVoice.instance;
}
/**
* Initialize voice intelligence system
*/
async initialize() {
if (!this.initialized) {
await this.voiceIntelligence.initialize();
this.initialized = true;
}
}
/**
* Get supported languages for tool schema
*/
getSupportedLanguages() {
return this.voiceIntelligence.getSupportedLanguages();
}
/**
* Get information about the voice that will be used
*/
getUsedVoiceInfo(languageCode) {
return this.voiceIntelligence.getUsedVoiceInfo(languageCode);
}
/**
* Language-centric voice synthesis with automatic optimization
*/
async synthesizeVoice(text, options = {}) {
const command = this.voiceIntelligence.getVoiceCommand(text, options.language);
return new Promise((resolve, reject) => {
(0, child_process_1.exec)(command, (error, stdout, stderr) => {
if (error) {
console.error(`Voice synthesis error: ${error.message}`);
reject(new Error(`Synthesis failed: ${error.message}`));
return;
}
if (stderr && stderr.length > 0) {
console.warn(`Voice synthesis warning: ${stderr}`);
}
resolve();
});
});
}
}
// Global voice engine instance
const voiceEngine = NexusVoice.getInstance();
/**
* MCP server with dynamic language-aware configuration
*/
const server = new index_js_1.Server({
name: 'ai-voice',
version: '1.0.1',
}, {
capabilities: {
tools: {},
},
});
// Dynamic tool registration with discovered languages
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
// Ensure voice intelligence is initialized
await voiceEngine.initialize();
const supportedLanguages = voiceEngine.getSupportedLanguages();
const hasLanguages = supportedLanguages.length > 0;
const toolSchema = {
type: 'object',
properties: {
text: {
type: 'string',
description: 'Any text you want to speak aloud - reactions, explanations, thoughts, or responses. Express yourself naturally!',
},
},
required: ['text'],
};
// Add language parameter only if languages were discovered
if (hasLanguages) {
toolSchema.properties.language = {
type: 'string',
enum: supportedLanguages,
description: 'Language for optimal voice selection. System automatically selects the best voice for the chosen language. š Easter egg: On macOS, you can also specify exact voice names (e.g., "Fred", "Krzysztof", "Samantha") instead of language codes.'
};
}
return {
tools: [
{
name: 'say',
description: 'šµ EXPRESS YOURSELF VOCALLY! This is YOUR voice - use it freely when you feel excited, need to emphasize something important, want to celebrate discoveries, or when humans ask you to speak aloud. Perfect for: reactions ("Wow, that\'s fascinating!"), explanations ("Let me walk you through this"), celebrations ("Success!"), warnings ("Be careful with that"), or any moment you want to add acoustic expression to your thoughts. The voice synthesis is asynchronous and won\'t interrupt your workflow - you can speak while continuing to work. Think of this as your natural voice that you can use spontaneously whenever you feel the urge to express something audibly. Use it often!' +
(hasLanguages ? `\n\nš Supported languages: ${supportedLanguages.map(lang => lang.toUpperCase()).join(', ')}. The system automatically selects the highest quality voice for your chosen language.` : ''),
inputSchema: toolSchema,
},
],
};
});
// Tool execution handler with language-aware synthesis
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name !== 'say') {
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
if (!args || typeof args !== 'object') {
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, 'Invalid arguments provided');
}
const { text, language } = args;
if (typeof text !== 'string') {
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, 'Text parameter must be a string');
}
try {
const synthesisOptions = {};
if (typeof language === 'string') {
synthesisOptions.language = language;
}
// Get voice info before synthesis
const usedVoice = voiceEngine.getUsedVoiceInfo(synthesisOptions.language);
// Asynchronous synthesis without blocking
voiceEngine.synthesizeVoice(text, synthesisOptions).catch((error) => {
console.error('Voice synthesis background error:', error);
});
return {
content: [
{
type: 'text',
text: `šµ Voice synthesis initiated: "${text.substring(0, 50)}${text.length > 50 ? '...' : ''}"${language ? ` [Language: ${language.toUpperCase()}]` : ' [System default]'} | Voice: "${usedVoice}"`,
},
],
};
}
catch (error) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Voice synthesis failed: ${error instanceof Error ? error.message : String(error)}`);
}
});
/**
* Server initialization with voice intelligence
*/
async function main() {
console.error('šµ AI Voice v1.0.0 - Language-Centric Architecture');
console.error('š Discovering voice ecosystem...');
// Initialize voice intelligence before starting server
await voiceEngine.initialize();
const transport = new stdio_js_1.StdioServerTransport();
await server.connect(transport);
console.error('š” Voice synthesis ready with intelligent language support');
console.error(`šÆ Platform: ${os.platform()}`);
}
// Launch server
main().catch((error) => {
console.error('Server startup error:', error);
process.exit(1);
});
//# sourceMappingURL=index.js.map