mcp-server-google-adk-sse-multi-tool-system

ammilam/mcp-server-google-adk-sse-multi-tool-system

3.3

If you are the rightful owner of mcp-server-google-adk-sse-multi-tool-system and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.

The Model Context Protocol (MCP) server is a TypeScript/Express server designed to provide a suite of tools for file operations, API calls, session data management, and weather information. It works in conjunction with a Google ADK agent to process user requests and perform various operations.

Tools
  1. file_system

    Handles file operations such as reading, writing, listing, and deleting files.

  2. api_call

    Facilitates making API calls to external services.

  3. session_data

    Manages session data storage and retrieval.

  4. weather

    Provides weather information for specified locations.

MCP Server and Google ADK Multi-Tool System

System Overview

The system consists of two main components:

  1. MCP Server: A TypeScript/Express server that provides tools for file operations, API calls, session data management, and weather information.
  2. Google ADK Agent: A Python agent that connects to the MCP server and uses its tools through a webhook interface.

The communication flow is:

  • Google ADK Agent receives user request
  • Agent determines which tool to use
  • Agent sends a request to MCP server's webhook endpoint
  • MCP server processes the request and performs the operation
  • MCP server returns result to the agent
  • Agent formats the response for the user

Running Everything

This guide will help you set up and run the MCP server and Google ADK agent system, including adding new tools to the system.

Prerequisites

  • Node.js (v16+) for the MCP server
  • Python (v3.9+) for the Google ADK agent
  • Google ADK SDK installed (pip install google-adk)
  • Google Project with Vertex AI enabled
  • IAM permissions for the Google ADK agent to access Vertex AI
  • GitHub or Gitlab access tokens as environment variables for the MCP server for repository access

Setting Up the Environment

  1. Set up the MCP serve and the Google ADK Agentr:

    # run this from the root directory of the project to setup the MCP server and the Google ADK agent
    ./scripts/setup.sh
    gcloud auth application-default login
    
  2. Configure environment variables:

    • For MCP server, make sure .env in contains:
      # Server Configuration
       PORT=9000
       BASE_DIR=./data
      
       # Repository Access Tokens, allows the MCP server to access private repositories
       GITHUB_ACCESS_TOKEN=your_github_token_here
       GITLAB_ACCESS_TOKEN=your_gitlab_token_here
      
       # Optional Configuration
       REPO_DIR=./repos
       MAX_EVENT_LISTENERS=100
      
    • For Google ADK agent, make sure .env in contains:
      GOOGLE_CLOUD_PROJECT="your-google-project-id"
      GOOGLE_CLOUD_LOCATION="us-central1"
      GOOGLE_GENAI_USE_VERTEXAI="True"
      MCP_SERVER_URL=http://localhost:9000
      
  3. Run MCP Server:

    # run this from the root directory of the project to start both the MCP server and the Google ADK agent
     ./scripts/run-mcp.sh
    
  4. Run Google ADK Agent:

     # run this from the root directory of this repository to start the Google ADK agent
     
     # will start this in web mode
     ./scripts/run-agent.sh web
     
     # will start this in terminal mode
     ./scripts/run-agent.sh run
    
  5. Open the web interface and select mcp_agent:

    • Go to http://localhost:8000 in browser
    • Select the mcp_agent from the dropdown
    • Chat with the agent to ensure it's working
  6. Test existing tools:

Running Google ADK Agent In Kubernetes

To run the Google ADK agent in Kubernetes, follow these steps:

  1. Build the Docker image:
    docker build -t mcp-agent:latest -f mcp_agent/Dockerfile .
    
  2. Push the image to Google Artifact Registry:
    # Build and tag the image
    

gcloud builds submit
--tag $GOOGLE_CLOUD_LOCATION-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/mcp-agent-repo/mcp-agent:latest
--project=$GOOGLE_CLOUD_PROJECT
.

Verify the image was pushed

gcloud artifacts docker images list
$GOOGLE_CLOUD_LOCATION-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/mcp-agent-repo
--project=$GOOGLE_CLOUD_PROJECT
--format=json

3. **Create a Kubernetes deployment**:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-agent
spec:
replicas: 1
selector:
 matchLabels:
   app: mcp-agent
template:
 metadata:
   labels:
     app: mcp-agent
 spec:
   serviceAccount: mcp-agent-sa
   containers:
   - name: mcp-agent
     imagePullPolicy: Always
     image: us-central1-docker.pkg.dev/your-gar-registry/mcp-agent-repo/mcp-agent:latest
     resources:
       limits:
         memory: "512Mi"
         cpu: "500m"
         ephemeral-storage: "1Gi"
       requests:
         memory: "256Mi"
         cpu: "250m"
         ephemeral-storage: "512Mi"
     ports:
     - containerPort: 8000
     env:
       - name: PORT
         value: "8000"
       - name: GOOGLE_CLOUD_PROJECT
         value: "your-google-project-id"
       - name: GOOGLE_CLOUD_LOCATION
         value: "us-central1"
       - name: GOOGLE_GENAI_USE_VERTEXAI
         value: "True"
       # Use a service name or an external access method for the MCP server
       - name: MCP_SERVER_URL
         value: "http://mcp-server-service:9000"
     volumeMounts:
     - name: credentials
       mountPath: "/app/credentials"
       readOnly: true
   volumes:
   - name: credentials
     secret:
       secretName: mcp-agent-credentials
---
apiVersion: v1
kind: Service
metadata:
name: mcp-agent
spec:       
type: LoadBalancer
ports:
 - port: 80
   targetPort: 8000
selector:
 app: mcp-agent

Adding a New Tool to MCP Server and Google ADK Agent System

This step-by-step guide will walk through the entire process of adding a new tool to the MCP server and making it available to a Google ADK agent. The guide below will use an image generation tool as our example.

3. Adding a New Tool to MCP Server

Let's add an image generation tool to the MCP server. We'll go through all required changes step-by-step.

Step 1: Create the Tool Implementation in MCP Server

First, let's add the core image generation functionality to the MCP server. Add this to the app.ts file in :

// 1. Add new type for ImageGeneration operation
type ImageGenerationOptions = {
  prompt: string;
  width?: number; 
  height?: number;
  style?: string;
  format?: 'png' | 'jpeg' | 'webp';
  negativePrompt?: string;
};

// 2. Add the image generation tool implementation
const imageGenerationTool = async (options: ImageGenerationOptions): Promise<{success: boolean; data?: any; error?: string}> => {
  try {
    // Validate parameters
    if (!options.prompt) {
      return { success: false, error: 'Image prompt is required' };
    }
    
    // Set defaults for missing options
    const width = options.width || 512;
    const height = options.height || 512;
    const format = options.format || 'png';
    const style = options.style || 'photorealistic';
    
    console.log(`Generating image for prompt: "${options.prompt}" with style: ${style}, dimensions: ${width}x${height}`);
    
    // For this example, we'll just mock the image generation
    // In a real implementation, you would call an API like Stable Diffusion or DALL-E
    const mockImageData = {
      prompt: options.prompt,
      imageUrl: `https://example.com/generated_images/${Date.now()}.${format}`,
      width,
      height,
      style,
      format,
      generatedAt: new Date().toISOString()
    };
    
    // In a real implementation, you might store the image file
    // For mock purposes, write a metadata file
    const metadataPath = validatePath(`images/metadata_${Date.now()}.json`);
    const dir = path.dirname(metadataPath);
    await fs.mkdir(dir, { recursive: true });
    await fs.writeFile(metadataPath, JSON.stringify(mockImageData, null, 2), 'utf-8');
    
    return {
      success: true,
      data: mockImageData
    };
  } catch (error: any) {
    console.error('Image generation error:', error);
    return {
      success: false, 
      error: error.message || 'Unknown error during image generation'
    };
  }
};
Step 2: Add Handler Function for Google ADK Webhook

Add this handler function to the MCP server to process requests from the Google ADK agent:

// Add this to the handler functions section
async function handleImageGenerationTool(parameters: any, sessionId: string) {
  // Validate required parameters
  if (!parameters.prompt) {
    return { 
      success: false, 
      error: 'Missing required parameter: prompt' 
    };
  }
  
  // Create options object for the image generation tool
  const options: ImageGenerationOptions = {
    prompt: parameters.prompt,
    width: parameters.width || 512,
    height: parameters.height || 512,
    style: parameters.style || 'photorealistic',
    format: parameters.format || 'png',
    negativePrompt: parameters.negativePrompt
  };
  
  return await imageGenerationTool(options);
}
Step 3: Update the Switch Statement in ADK Webhook

Now, add a new case to the switch statement in the /api/adk-webhook route handler:

// Find the switch statement in app.post('/api/adk-webhook', ...) 
switch (toolName) {
  case 'file_system':
    result = await handleFileSystemTool(parameters, mcpSessionId);
    break;
  case 'api_call':
    result = await handleApiTool(parameters, mcpSessionId);
    break;
  case 'session_data':
    result = await handleSessionDataTool(parameters, mcpSessionId);
    break;
  case 'weather':
    result = await handleWeatherTool(parameters, mcpSessionId);
    break;
  // Add the new case for image generation
  case 'image_generation':
    result = await handleImageGenerationTool(parameters, mcpSessionId);
    break;
  default:
    result = {
      success: false,
      error: `Unknown tool name: ${toolName}`
    };
}
Step 4: Add Endpoint for Direct Access

Add a dedicated endpoint for direct access to the image generation tool:

// Add this route to the app
app.post('/api/session/:sessionId/image', async (req, res) => {
  const { sessionId } = req.params;
  const session = getSessionAndUpdate(sessionId);

  if (!session) {
    return res.status(404).json({
      success: false,
      error: 'Session not found'
    });
  }

  const options: ImageGenerationOptions = req.body;

  if (!options.prompt) {
    return res.status(400).json({
      success: false,
      error: 'Image prompt is required'
    });
  }

  const result = await imageGenerationTool(options);

  if (result.success) {
    // Emit an event for SSE clients
    emitEvent(sessionId, 'image-generation', {
      imageUrl: result.data.imageUrl,
      prompt: options.prompt,
      timestamp: new Date().toISOString()
    });
    
    res.status(200).json(result);
  } else {
    res.status(400).json(result);
  }
});
Step 5: Update API Documentation

Add the new tool to the API documentation in the /api/help endpoint:

// Find the endpoints array in the helpDocs object
endpoints: [
  // Add these new entries
  {
    path: "/api/session/{sessionId}/image",
    method: "POST",
    description: "Generate an image from a text prompt",
    parameters: [
      {
        name: "sessionId",
        in: "path",
        required: true,
        description: "Session identifier"
      }
    ],
    requestBodyExample: {
      prompt: "A beautiful sunset over mountains",
      width: 512,
      height: 512,
      style: "photorealistic",
      format: "png",
      negativePrompt: "blur, low quality"
    },
    responseExample: {
      success: true,
      data: {
        prompt: "A beautiful sunset over mountains",
        imageUrl: "https://example.com/generated_images/1717451623456.png",
        width: 512,
        height: 512,
        style: "photorealistic",
        format: "png",
        generatedAt: "2025-06-03T12:00:00.000Z"
      }
    },
    curlExample: `curl -X POST ${baseUrl}/api/session/{sessionId}/image \\
  -H "Content-Type: application/json" \\
  -d '{"prompt": "A beautiful sunset over mountains", "style": "photorealistic"}'`
  },
  // Add the Google ADK webhook documentation for image_generation tool
  {
    path: "/api/adk-webhook",
    method: "POST",
    description: "Webhook for Google ADK image generation",
    requestBodyExample: {
      session_id: "google-adk-session-123",
      tool_name: "image_generation",
      parameters: {
        prompt: "A beautiful sunset over mountains",
        width: 512,
        height: 512,
        style: "photorealistic"
      },
      request_id: "request-123"
    },
    responseExample: {
      success: true,
      data: {
        prompt: "A beautiful sunset over mountains",
        imageUrl: "https://example.com/generated_images/1717451623456.png",
        width: 512,
        height: 512,
        style: "photorealistic",
        format: "png",
        generatedAt: "2025-06-03T12:00:00.000Z"
      },
      mcp_session_id: "550e8400-e29b-41d4-a716-446655440000",
      request_id: "request-123"
    },
    notes: "This endpoint is used by the Google ADK agent to generate images."
  }
],

4. Adding Tool Support to Google ADK Agent

Now let's add the image generation tool to the Google ADK agent, which is found in .

Step 1: Add Method to MCPToolkit Class

First, add a new method to the file to interact with the image generation tool:

# Add this method to the MCPToolkit class in mcp_toolkit.py
def generate_image(self, prompt: str, width: int = 512, height: int = 512, 
                  style: str = "photorealistic", format: str = "png", 
                  negative_prompt: str = None) -> Dict:
    """Generate an image from a text prompt using the MCP server"""
    params = {
        "prompt": prompt,
        "width": width,
        "height": height,
        "style": style,
        "format": format
    }
    
    if negative_prompt:
        params["negativePrompt"] = negative_prompt
        
    return self.execute_tool("image_generation", params)
Step 2: Add Tool Function to tools.py

Create a new tool function in that will be exposed to the ADK agent:

# Add this to the tools.py file
def mcp_generate_image(prompt: str, style: str = "photorealistic", width: int = 512, height: int = 512) -> dict:
    """Generates an image from a text prompt.
    
    Args:
        prompt: Text description of the image to generate
        style: Style for the image (e.g., photorealistic, cartoon, sketch)
        width: Width of the output image in pixels
        height: Height of the output image in pixels
        
    Returns:
        dict: A dictionary with status ('success' or 'error') and either image info or error message
    """
    try:
        result = mcp_toolkit.generate_image(
            prompt=prompt, 
            style=style,
            width=width, 
            height=height
        )
        
        if result.get("success"):
            image_data = result.get("data", {})
            return {
                "status": "success",
                "image_url": image_data.get("imageUrl"),
                "message": f"Generated image for prompt: '{prompt}' in {style} style."
            }
        else:
            return {
                "status": "error",
                "error_message": result.get("error", "Unknown error generating image")
            }
    except Exception as e:
        logger.error(f"Error in mcp_generate_image: {str(e)}")
        return {
            "status": "error",
            "error_message": f"Exception: {str(e)}"
        }
Step 3: Register the Tool with the Agent

Update file to include the new tool:

# Add to the imports in agent.py
from .tools import (
    mcp_read_file, 
    mcp_write_file, 
    mcp_list_files, 
    mcp_delete_file,
    mcp_get_weather,
    mcp_call_api,
    mcp_store_data,
    mcp_store_number,
    mcp_store_boolean,
    mcp_retrieve_data,
    mcp_generate_image  # Add this import
)

# Update the agent definition
agent = Agent(
    name="mcp_agent",
    model="gemini-2.0-flash",
    description="Agent that can handle weather, time, and interact with a Model Control Protocol server",
    instruction="""I can help you with various tasks through my integration with the MCP server.
I can:
- Get current time in different cities
- Check weather conditions in locations
- Read, write, list, and delete files
- Make API calls to external services
- Store and retrieve data in a session (text, numbers, or boolean values)
- Generate images from text descriptions

When you ask me about files, I'll use the appropriate file operation tools.
When you ask me about weather, I'll look up the latest conditions.
When you want to store information for later, I'll use session storage.
When you ask me to create an image, I'll generate one based on your description.
""",
    tools=[
        # MCP file system tools
        mcp_read_file,
        mcp_write_file,
        mcp_list_files,
        mcp_delete_file,
        
        # MCP API tools
        mcp_call_api,
        mcp_get_weather,
        
        # MCP session tools
        mcp_store_data,
        mcp_store_number, 
        mcp_store_boolean, 
        mcp_retrieve_data,
        
        # New image generation tool
        mcp_generate_image  # Add this tool
    ]
)

5. Testing and Debugging

Testing the Tool Directly with cURL

Test the image generation endpoint directly using cURL:

# First create a session
export SESSION=$(curl -X POST http://localhost:8080/api/session | jq -r '.sessionId')

# Then call the image generation endpoint
curl -X POST http://localhost:8080/api/session/$SESSION/image \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A beautiful sunset over mountains",
    "style": "photorealistic"
  }'
Testing through the ADK Webhook

Test the image generation through the ADK webhook:

curl -X POST http://localhost:8080/api/adk-webhook \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "test-session-123",
    "tool_name": "image_generation",
    "parameters": {
      "prompt": "A beautiful sunset over mountains",
      "style": "photorealistic"
    },
    "request_id": "test-request-1"
  }'
Running the Agent and Testing the Tool

Run the agent and test the image generation tool with a prompt:

cd /mcp-server-google-adk-multi-tool-system
python -m mcp_agent.main

Then when the agent is running, try:

You: Generate an image of a cat playing piano

6. How Current Integrations Work

Let's examine how the existing integrations work in the system.

MCP Server Components
  1. Session Management:

    • Each client gets a unique session ID
    • Sessions store user data and have a 30-minute expiration
    • Sessions are managed using a Map in memory
  2. Tool Implementation:

    • Each tool (fileSystemTool, apiTool, etc.) is implemented as an async function
    • Tools handle validation, processing, and error handling
    • Tools return a standardized response object with success, data, and optional error fields
  3. Endpoints:

    • Each tool has a dedicated endpoint (e.g., /api/session/:sessionId/filesystem)
    • A central webhook endpoint (/api/adk-webhook) handles requests from Google ADK
    • The webhook endpoint routes requests to appropriate handler functions based on tool_name
  4. Server-Sent Events (SSE):

    • The /api/sse/:sessionId endpoint enables real-time updates
    • The emitEvent function sends events to connected clients
    • Clients can listen for events through an EventSource connection

Google ADK Agent Components

  1. MCPToolkit Class:

    • Manages communication with the MCP server
    • Handles session creation and management
    • Provides methods for each tool operation
    • Maintains an SSE connection for real-time updates
  2. Tool Functions:

    • Each tool function (mcp_read_file, mcp_get_weather, etc.) is a wrapper around MCPToolkit methods
    • Functions follow Google ADK format with proper documentation and type hints
    • Functions return standardized response objects with status and additional fields
  3. Agent Definition:

    • The Agent class from Google ADK SDK defines agent capabilities
    • Agent registers tool functions and provides instructions
    • Agent handles NLU (Natural Language Understanding) for user inputs
  4. Main Runner:

    • Initializes the agent and toolkit
    • Sets up event listeners
    • Manages the conversation loop
    • Handles errors and cleanup

Data Flow Between Components

  1. User Request Flow:

    • User sends text query to the agent
    • Agent processes the query and determines the appropriate tool
    • Agent calls the tool function with extracted parameters
    • Tool function calls MCPToolkit method
    • MCPToolkit sends request to MCP server webhook
    • MCP server processes request and returns result
    • Result flows back through the same chain in reverse
  2. Server-Sent Events Flow:

    • MCP server processes an operation
    • Server emits event with emitEvent
    • SSE connection transmits event to client
    • MCPToolkit receives event in _sse_worker thread
    • Event callback processes the event

Summary

Adding a new tool to the MCP server and Google ADK agent involves these key steps:

  1. MCP Server:

    • Create the tool implementation function
    • Add a handler function for the webhook
    • Update the webhook switch statement
    • Add a dedicated endpoint if needed
    • Update API documentation
  2. Google ADK Agent:

    • Add a method to the MCPToolkit class
    • Create a tool function in tools.py
    • Register the tool with the agent
    • Update agent instructions

By following this guide, you can easily extend the MCP server and Google ADK agent with new tools and capabilities. The modular design makes it straightforward to add new features while maintaining a consistent interface and error handling approach.

Author: Andrew Milam