Skip to main content
Glama

MindBridge MCP Server

google.ts8.35 kB
import { GetSecondOpinionInput, LLMResponse, LLMError, GoogleConfig } from '../types.js'; import { LLMProvider, handleProviderError, createSuccessResponse } from './base.js'; import * as fs from 'fs'; // Custom interfaces for Google AI API responses // Note: We're using 'any' for flexibility with the Gemini API format interface GoogleAIError { error: { code: number; message: string; status: string; }; } interface GoogleAIChatResponse { candidates?: Array<{ content?: { parts?: Array<{ text?: string; }>; }; finishReason?: string; }>; usageMetadata?: { promptTokenCount?: number; totalTokenCount?: number; promptTokensDetails?: Array<{ modality?: string; tokenCount?: number; }>; }; modelVersion?: string; } /** * Google Gemini Provider * * Note: Gemini 2.5 models (gemini-2.5-pro-exp-03-25, gemini-2.5-pro-preview-03-25) are currently * not fully supported through the REST API. They return a 200 status code but no content. * For best results with these models, use the @google/genai library directly. */ export class GoogleProvider implements LLMProvider { private apiKey: string; private baseUrl: string; constructor(config: GoogleConfig) { this.apiKey = config.apiKey; this.baseUrl = config.baseUrl || 'https://generativelanguage.googleapis.com/v1beta'; } public supportsReasoningEffort(): boolean { return true; // Gemini models support reasoning effort } public supportsReasoningEffortForModel(_model: string): boolean { // All Gemini models support reasoning through prompting // They don't return the thinking process in the API response, but they do reason internally return true; } public getAvailableModels(): string[] { return [ 'gemini-1.5-pro', 'gemini-1.5-flash', 'gemini-1.0-pro', 'gemini-1.0-pro-vision', 'gemini-2.0-flash', 'gemini-2.0-flash-thinking-exp' // Temporarily removed until we figure out how to get responses from them // 'gemini-2.5-pro-preview-03-25', // 'gemini-2.5-pro-exp-03-25' // Free experimental model ]; } public isValidModel(model: string): boolean { return this.getAvailableModels().includes(model); } private isGoogleAIError(obj: unknown): obj is GoogleAIError { return typeof obj === 'object' && obj !== null && 'error' in obj && typeof (obj as GoogleAIError).error === 'object' && 'message' in (obj as GoogleAIError).error; } private async generateGeminiResponse( model: string, prompt: string, systemPrompt: string | undefined, temperature: number | undefined, maxTokens: number | undefined, reasoningEffort?: string ): Promise<string> { // Store the model name for error handling const modelName = model; // Prepare the request body let requestBody: any; // Simplified request format based on the curl example if (this.supportsReasoningEffortForModel(model) && reasoningEffort) { // For thinking models, we'll add a system prompt to encourage detailed reasoning const reasoningDepth = reasoningEffort === 'high' ? 'extremely detailed' : reasoningEffort === 'medium' ? 'detailed' : 'brief'; // Create a chat-like structure with system prompt const contents: any[] = []; // Add system prompt if provided if (systemPrompt) { contents.push({ role: 'user', parts: [{ text: systemPrompt }] }); } // Add a special prompt to encourage reasoning contents.push({ role: 'user', parts: [{ text: `Please provide ${reasoningDepth} step-by-step reasoning before giving your final answer. Start with "Reasoning:" and end with "Answer:" to clearly separate your thought process from your final response.` }] }); // Add the user prompt contents.push({ role: 'user', parts: [{ text: prompt }] }); requestBody = { contents, generationConfig: { temperature, maxOutputTokens: maxTokens, topK: 40, topP: 0.95 } }; } else { // For regular models, use a simpler format based on the curl example requestBody = { contents: [ { role: 'user', parts: [ { text: prompt } ] } ], generationConfig: { temperature, maxOutputTokens: maxTokens, topK: 40, topP: 0.95 } }; // Add system prompt if provided if (systemPrompt) { requestBody.systemInstruction = { parts: [{ text: systemPrompt }] }; } } // Use the correct API endpoint // Make sure we're using the correct endpoint format const apiEndpoint = `${this.baseUrl}/models/${model}:generateContent?key=${this.apiKey}`; console.log(`Sending request to Google API: ${apiEndpoint}`); const requestBodyJson = JSON.stringify(requestBody); console.log('Request body:', requestBodyJson); const response = await fetch(apiEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: requestBodyJson }); // Log the response status and headers console.log('Response status:', response.status); console.log('Response headers:', JSON.stringify(Object.fromEntries([...response.headers.entries()]))); if (!response.ok) { try { const errorText = await response.text(); console.error('Google API error response:', errorText); try { const errorData = JSON.parse(errorText); throw new Error( this.isGoogleAIError(errorData) ? errorData.error.message : `Google AI request failed: ${errorText}` ); } catch (jsonError) { throw new Error(`Google API request failed with status ${response.status}: ${errorText}`); } } catch (error) { throw new Error(`Google API request failed: ${error instanceof Error ? error.message : String(error)}`); } } try { const responseText = await response.text(); console.log('Google API response:', responseText); // Also log to a file for debugging try { fs.writeFileSync('google-api-response.json', responseText); console.log('Response saved to google-api-response.json'); } catch (error) { console.error('Error saving response to file:', error); } const data = JSON.parse(responseText) as GoogleAIChatResponse; // Check if we have a response with metadata but no content (common with thinking models) if (data.usageMetadata && !data.candidates?.[0]?.content?.parts?.[0]?.text) { // This is likely a thinking model that doesn't return content via API if (reasoningEffort) { return `The model ${modelName} supports thinking capabilities in Google AI Studio, ` + `but the thinking process is not provided in the API output. ` + `The API call was successful, but no content was returned. ` + `Consider using the @google/genai library directly for better results.`; } else { throw new Error('No response content received from Google AI'); } } const text = data.candidates?.[0]?.content?.parts?.[0]?.text; if (!text) { throw new Error('No response received from Google AI'); } return text; } catch (error) { throw new Error(`Failed to parse Google AI response: ${error instanceof Error ? error.message : String(error)}`); } } public async getResponse(params: GetSecondOpinionInput): Promise<LLMResponse | LLMError> { try { const systemPrompt = params.systemPrompt || undefined; const responseText = await this.generateGeminiResponse( params.model, params.prompt, systemPrompt, params.temperature, params.maxTokens, params.reasoning_effort as string ); return createSuccessResponse(responseText); } catch (error) { return handleProviderError(error, 'Google AI'); } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pinkpixel-dev/mindbridge-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server