@stevekaplanai/google-ai-mcp
Version:
Model Context Protocol server for Google AI services (VEO 3, Imagen 4, Gemini, Lyria 2)
495 lines • 19 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
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 zod_1 = require("zod");
const google_auth_library_1 = require("google-auth-library");
const axios_1 = __importDefault(require("axios"));
const dotenv_1 = __importDefault(require("dotenv"));
const imagen_service_js_1 = require("./services/imagen.service.js");
const veo_service_js_1 = require("./services/veo.service.js");
const lyria_service_js_1 = require("./services/lyria.service.js");
// Load environment variables
dotenv_1.default.config();
// Configuration schema
const ConfigSchema = zod_1.z.object({
projectId: zod_1.z.string(),
location: zod_1.z.string().default('us-central1'),
credentials: zod_1.z.object({
type: zod_1.z.string(),
project_id: zod_1.z.string(),
private_key_id: zod_1.z.string(),
private_key: zod_1.z.string(),
client_email: zod_1.z.string(),
client_id: zod_1.z.string(),
auth_uri: zod_1.z.string(),
token_uri: zod_1.z.string(),
auth_provider_x509_cert_url: zod_1.z.string(),
client_x509_cert_url: zod_1.z.string(),
}).optional(),
});
// Configuration
let config;
let auth = null;
let imagenService = null;
let veoService = null;
let lyriaService = null;
const USE_MOCK = process.env.USE_MOCK === 'true' || !process.env.GOOGLE_CLOUD_PROJECT;
// Initialize configuration
function initializeConfig() {
const projectId = process.env.GOOGLE_CLOUD_PROJECT || 'mock-project';
const location = process.env.GOOGLE_CLOUD_LOCATION || 'us-central1';
let credentials;
if (process.env.GOOGLE_APPLICATION_CREDENTIALS_JSON) {
try {
credentials = JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS_JSON);
}
catch (error) {
console.error('Failed to parse credentials JSON:', error);
}
}
config = ConfigSchema.parse({
projectId,
location,
credentials,
});
if (!USE_MOCK && credentials) {
auth = new google_auth_library_1.GoogleAuth({
credentials,
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
});
}
// Initialize Imagen service
imagenService = new imagen_service_js_1.ImagenService('', {
mockMode: USE_MOCK,
debug: true
});
// Initialize VEO service
veoService = new veo_service_js_1.VeoService('', {
mockMode: USE_MOCK,
debug: true
});
// Initialize Lyria service
lyriaService = new lyria_service_js_1.LyriaService({
mockMode: USE_MOCK,
debug: true
});
}
// MCP Server setup
const server = new index_js_1.Server({
name: 'google-ai-mcp-server',
version: '1.0.0',
}, {
capabilities: {
tools: {},
},
});
// Tool definitions
const tools = [
{
name: 'veo_generate_video',
description: 'Generate videos using Google VEO 3 (5-8 seconds with audio)',
inputSchema: {
type: 'object',
properties: {
prompt: { type: 'string', description: 'Text prompt for video generation' },
imageBase64: { type: 'string', description: 'Base64-encoded image for image-to-video generation' },
duration: { type: 'number', minimum: 5, maximum: 8, default: 5, description: 'Video duration in seconds' },
aspectRatio: { enum: ['16:9', '9:16', '1:1'], default: '16:9', description: 'Video aspect ratio' },
sampleCount: { type: 'number', minimum: 1, maximum: 4, default: 1, description: 'Number of videos to generate' },
negativePrompt: { type: 'string', description: 'What to avoid in the generation' },
personGeneration: { enum: ['allow', 'disallow'], default: 'allow', description: 'Whether to allow person generation' },
outputStorageUri: { type: 'string', description: 'GCS bucket URI for output' },
},
required: ['prompt'],
},
},
{
name: 'imagen_generate_image',
description: 'Generate photorealistic images using Google Imagen 4',
inputSchema: {
type: 'object',
properties: {
prompt: { type: 'string', description: 'Text prompt for image generation' },
sampleCount: { type: 'number', minimum: 1, maximum: 8, default: 1, description: 'Number of images to generate' },
aspectRatio: { enum: ['1:1', '16:9', '9:16', '4:3', '3:4'], default: '1:1', description: 'Image aspect ratio' },
negativePrompt: { type: 'string', description: 'What to avoid in the generation' },
personGeneration: { enum: ['allow', 'disallow'], default: 'allow', description: 'Whether to allow person generation' },
language: { type: 'string', default: 'en', description: 'Language for the prompt' },
outputStorageUri: { type: 'string', description: 'GCS bucket URI for output' },
},
required: ['prompt'],
},
},
{
name: 'gemini_generate_text',
description: 'Generate text using Google Gemini models',
inputSchema: {
type: 'object',
properties: {
prompt: { type: 'string', description: 'Text prompt for Gemini' },
model: { enum: ['gemini-1.5-pro', 'gemini-1.5-flash', 'gemini-2.0-flash-exp'], default: 'gemini-1.5-flash', description: 'Gemini model to use' },
temperature: { type: 'number', minimum: 0, maximum: 2, default: 0.7, description: 'Temperature for randomness' },
maxTokens: { type: 'number', minimum: 1, maximum: 8192, default: 2048, description: 'Maximum tokens to generate' },
systemInstruction: { type: 'string', description: 'System instruction for the model' },
},
required: ['prompt'],
},
},
{
name: 'lyria_generate_music',
description: 'Generate music using Google Lyria 2 (up to 60 seconds)',
inputSchema: {
type: 'object',
properties: {
textPrompt: { type: 'string', description: 'Text description of the music to generate' },
musicalStructure: { enum: ['verse-chorus', 'free-form', 'instrumental'], default: 'free-form', description: 'Musical structure' },
genre: { type: 'string', description: 'Musical genre' },
mood: { type: 'string', description: 'Mood of the music' },
tempo: { enum: ['slow', 'medium', 'fast'], description: 'Tempo of the music' },
durationSeconds: { type: 'number', minimum: 1, maximum: 60, default: 30, description: 'Duration in seconds' },
outputStorageUri: { type: 'string', description: 'GCS bucket URI for output' },
},
required: ['textPrompt'],
},
},
{
name: 'check_operation_status',
description: 'Check the status of a long-running operation',
inputSchema: {
type: 'object',
properties: {
operationName: { type: 'string', description: 'Operation name from a previous request' },
},
required: ['operationName'],
},
},
];
// List tools handler
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
return { tools };
});
// Tool execution handler
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'veo_generate_video':
return await handleVeoGenerate(args);
case 'imagen_generate_image':
return await handleImagenGenerate(args);
case 'gemini_generate_text':
return await handleGeminiGenerate(args);
case 'lyria_generate_music':
return await handleLyriaGenerate(args);
case 'check_operation_status':
return await handleCheckOperation(args);
default:
throw new Error(`Unknown tool: ${name}`);
}
}
catch (error) {
console.error(`Error executing tool ${name}:`, error);
return {
content: [
{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
};
}
});
// VEO 3 Handler
async function handleVeoGenerate(args) {
try {
if (!veoService) {
throw new Error('VEO service not initialized');
}
const result = await veoService.generateVideo({
prompt: args.prompt,
imageBase64: args.imageBase64,
duration: args.duration,
aspectRatio: args.aspectRatio,
sampleCount: args.sampleCount,
negativePrompt: args.negativePrompt,
personGeneration: args.personGeneration,
outputStorageUri: args.outputStorageUri
});
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
}
catch (error) {
console.error('VEO generation error:', error);
throw error;
}
}
// Imagen 4 Handler
async function handleImagenGenerate(args) {
try {
if (!imagenService) {
throw new Error('Imagen service not initialized');
}
const result = await imagenService.generateImage(args.prompt, args.aspectRatio, args.sampleCount, args.negativePrompt, args.personGeneration, args.language, args.outputStorageUri);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
}
catch (error) {
console.error('Imagen generation error:', error);
throw error;
}
}
// Gemini Handler
async function handleGeminiGenerate(args) {
if (USE_MOCK) {
// Mock response for testing
const mockResponses = {
'hello': 'Hello! How can I assist you today?',
'test': 'This is a mock response from Gemini. In production, this would be a real AI-generated response.',
'default': `Based on your prompt "${args.prompt}", here's a mock Gemini response. When connected to the real API, Gemini will provide intelligent, contextual responses.`,
};
const response = mockResponses[args.prompt.toLowerCase()] || mockResponses.default;
return {
content: [
{
type: 'text',
text: JSON.stringify({
text: response,
model: args.model || 'gemini-1.5-flash',
usage: {
promptTokens: args.prompt.length,
completionTokens: response.length,
totalTokens: args.prompt.length + response.length,
},
finishReason: 'STOP',
safety: {
categories: [],
blocked: false,
},
}, null, 2),
},
],
};
}
// Real Gemini API implementation
try {
if (!auth) {
throw new Error('Google Cloud authentication not configured');
}
const token = await auth.getAccessToken();
const model = args.model || 'gemini-1.5-flash';
const endpoint = `https://${config.location}-aiplatform.googleapis.com/v1/projects/${config.projectId}/locations/${config.location}/publishers/google/models/${model}:generateContent`;
const requestBody = {
contents: [
{
role: 'user',
parts: [
{
text: args.prompt,
},
],
},
],
generationConfig: {
temperature: args.temperature || 0.7,
maxOutputTokens: args.maxTokens || 2048,
},
};
if (args.systemInstruction) {
requestBody.systemInstruction = {
parts: [
{
text: args.systemInstruction,
},
],
};
}
const response = await axios_1.default.post(endpoint, requestBody, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
}
catch (error) {
console.error('Gemini API error:', error);
throw error;
}
}
// Lyria 2 Handler
async function handleLyriaGenerate(args) {
if (USE_MOCK) {
// Mock response for testing
const operationId = `mock-lyria-${Date.now()}`;
return {
content: [
{
type: 'text',
text: JSON.stringify({
operationName: `projects/${config.projectId}/locations/${config.location}/operations/${operationId}`,
status: 'PROCESSING',
metadata: {
createTime: new Date().toISOString(),
target: 'lyria-music-generation',
verb: 'generate',
},
done: false,
response: {
audio: {
uri: `gs://mock-bucket/lyria-output/${operationId}/audio.mp3`,
metadata: {
duration: args.durationSeconds || 30,
format: 'mp3',
sampleRate: 44100,
bitrate: '320kbps',
genre: args.genre,
mood: args.mood,
tempo: args.tempo,
structure: args.musicalStructure || 'free-form',
},
},
prompt: args.textPrompt,
modelVersion: 'lyria-2-latest',
},
}, null, 2),
},
],
};
}
// TODO: Implement real Lyria 2 API call when available
// Note: Lyria 2 may have limited availability
return {
content: [
{
type: 'text',
text: 'Lyria 2 API implementation pending. Please use mock mode (USE_MOCK=true) for testing.',
},
],
};
}
// Operation Status Handler
async function handleCheckOperation(args) {
const { operationName } = args;
if (USE_MOCK) {
// Mock operation status
const mockStatuses = ['PROCESSING', 'DONE', 'FAILED'];
const randomStatus = mockStatuses[Math.floor(Math.random() * mockStatuses.length)];
const response = {
name: operationName,
metadata: {
createTime: new Date(Date.now() - 60000).toISOString(), // 1 minute ago
updateTime: new Date().toISOString(),
},
done: randomStatus === 'DONE',
};
if (randomStatus === 'DONE') {
response.response = {
status: 'SUCCESS',
outputUri: `gs://mock-bucket/output/${operationName.split('/').pop()}`,
completionTime: new Date().toISOString(),
};
}
else if (randomStatus === 'FAILED') {
response.error = {
code: 500,
message: 'Mock operation failed for testing purposes',
};
}
else {
response.metadata.progress = Math.floor(Math.random() * 100);
}
return {
content: [
{
type: 'text',
text: JSON.stringify(response, null, 2),
},
],
};
}
// Real operation status check
try {
if (!auth) {
throw new Error('Google Cloud authentication not configured');
}
const token = await auth.getAccessToken();
const endpoint = `https://${config.location}-aiplatform.googleapis.com/v1/${operationName}`;
const response = await axios_1.default.get(endpoint, {
headers: {
'Authorization': `Bearer ${token}`,
},
});
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
}
catch (error) {
console.error('Operation status check error:', error);
throw error;
}
}
// Main function
async function main() {
try {
// Initialize configuration
initializeConfig();
console.error(`Google AI MCP Server v1.0.0`);
console.error(`Project: ${config.projectId}`);
console.error(`Location: ${config.location}`);
console.error(`Mode: ${USE_MOCK ? 'MOCK' : 'PRODUCTION'}`);
if (USE_MOCK) {
console.error('\n⚠️ Running in MOCK mode. Set GOOGLE_CLOUD_PROJECT to use real APIs.');
}
else {
console.error('\n✓ Connected to Google Cloud');
}
console.error('\nAvailable tools:');
tools.forEach(tool => {
console.error(` - ${tool.name}: ${tool.description}`);
});
// Start the server
const transport = new stdio_js_1.StdioServerTransport();
await server.connect(transport);
console.error('\nGoogle AI MCP Server running on stdio');
}
catch (error) {
console.error('Failed to start server:', error);
process.exit(1);
}
}
// Start the server
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
//# sourceMappingURL=index.js.map