Gemini Integration Workshop Exercises

workshop claude-code gemini typescript hands-on

Workshop Overview

This workshop teaches TypeScript developers how to integrate Claude Code with Google’s Gemini models to leverage their 1M+ context window capabilities. Through hands-on exercises, you’ll learn to build applications that can process entire codebases, analyze large documents, and orchestrate multiple AI models effectively.

Prerequisites

  • Node.js 18+ and npm/pnpm
  • Basic TypeScript knowledge
  • Claude Code CLI installed
  • Google Cloud account with Gemini API access

Setup Instructions

# Clone the workshop repository
git clone https://github.com/your-org/claude-gemini-workshop
cd claude-gemini-workshop
 
# Install dependencies
pnpm install
 
# Copy environment template
cp .env.example .env
 
# Add your API keys to .env:
# CLAUDE_API_KEY=your_claude_key
# GEMINI_API_KEY=your_gemini_key

Exercise 1: Basic MCP Server Setup

Objective: Create a basic MCP server that bridges Claude Code with Gemini API.

Exercise 1.1: Create Your First MCP Server

// exercises/01-basic-mcp/server.ts
// TODO: Import required packages
// TODO: Initialize Gemini client
// TODO: Create MCP server with basic configuration
 
// Your task: Complete this server implementation
export function createBasicMCPServer() {
  // Hint: Use McpServer from @modelcontextprotocol/sdk
  // Initialize with name, version, and description
}
 
// Test your implementation
// Run: pnpm test:exercise-1.1

Exercise 1.2: Implement a Simple Tool

// exercises/01-basic-mcp/tools.ts
// TODO: Implement a tool that sends text to Gemini and returns the response
 
// Your task: Complete this tool implementation
export function createAnalysisTool(server: McpServer) {
  // Hint: Use server.tool() method
  // Define inputSchema with zod
  // Call Gemini API in the handler
}
 
// Expected behavior:
// - Accept 'content' and 'prompt' as inputs
// - Send to Gemini 1.5 Flash
// - Return the response

Exercise 1.3: Register and Test

# Register your MCP server with Claude Code
# TODO: Write the command to register your server
 
# Test the integration
# TODO: Write a query to Claude that uses your new tool

Success Criteria:

  • MCP server starts without errors
  • Tool appears in Claude’s available tools
  • Can successfully call Gemini through Claude

Exercise 2: Large Context Processing

Objective: Build a system that can analyze entire codebases using Gemini’s 1M token context.

Exercise 2.1: Codebase Loader

// exercises/02-large-context/codebase-loader.ts
import { glob } from 'glob';
import fs from 'fs/promises';
 
interface CodebaseFile {
  path: string;
  content: string;
  language: string;
}
 
// TODO: Implement a function that loads an entire codebase
export async function loadCodebase(
  rootPath: string,
  options?: {
    include?: string[];
    exclude?: string[];
    maxFileSize?: number;
  }
): Promise<CodebaseFile[]> {
  // Your implementation here
  // Hint: Use glob patterns to find files
  // Filter by file extensions
  // Read file contents
  // Detect language from extension
}
 
// Bonus: Implement token counting
export function estimateTokens(files: CodebaseFile[]): number {
  // Implement token estimation
}

Exercise 2.2: Context-Aware Analysis Tool

// exercises/02-large-context/analysis-tool.ts
// TODO: Create an MCP tool that analyzes large codebases
 
export function createCodebaseAnalysisTool(server: McpServer) {
  server.tool(
    "analyze-codebase",
    {
      description: "Analyze an entire codebase with Gemini",
      inputSchema: z.object({
        path: z.string(),
        analysisType: z.enum([
          "architecture",
          "security",
          "performance",
          "quality"
        ]),
        // TODO: Add more schema fields
      })
    },
    async (inputs) => {
      // TODO: Implement the analysis logic
      // 1. Load the codebase
      // 2. Check token count
      // 3. Choose appropriate Gemini model
      // 4. Send to Gemini with structured prompt
      // 5. Return formatted results
    }
  );
}

Exercise 2.3: Implement Caching

// exercises/02-large-context/cache.ts
// TODO: Implement a caching layer for large context operations
 
interface CacheEntry {
  key: string;
  content: string;
  timestamp: number;
  metadata: Record<string, any>;
}
 
export class ContextCache {
  // TODO: Implement cache methods
  async get(key: string): Promise<CacheEntry | null> {
    // Your implementation
  }
  
  async set(key: string, content: string, metadata?: Record<string, any>): Promise<void> {
    // Your implementation
  }
  
  async invalidate(pattern: string): Promise<number> {
    // Your implementation
  }
}
 
// Bonus: Implement LRU eviction
// Bonus: Add persistent storage option

Success Criteria:

  • Can load codebases up to 1M tokens
  • Correctly chooses Gemini model based on size
  • Cache reduces repeated processing time by >50%

Exercise 3: Model Orchestration

Objective: Build an intelligent routing system that uses Claude for reasoning and Gemini for large context processing.

Exercise 3.1: Task Router

// exercises/03-orchestration/router.ts
interface Task {
  id: string;
  type: 'analysis' | 'generation' | 'question' | 'comparison';
  content: string;
  context?: string;
  requirements: string[];
}
 
interface RoutingDecision {
  primary: 'claude' | 'gemini';
  strategy: 'direct' | 'sequential' | 'parallel';
  reason: string;
}
 
// TODO: Implement intelligent task routing
export class TaskRouter {
  async route(task: Task): Promise<RoutingDecision> {
    // TODO: Implement routing logic based on:
    // - Content size (token count)
    // - Task type
    // - Complexity indicators
    // - Performance requirements
  }
  
  async execute(task: Task): Promise<any> {
    // TODO: Execute based on routing decision
  }
}
 
// Test cases to handle:
// 1. Small reasoning task -> Claude only
// 2. Large document analysis -> Gemini then Claude
// 3. Code generation -> Claude with Gemini context

Exercise 3.2: Parallel Processing

// exercises/03-orchestration/parallel.ts
// TODO: Implement parallel model execution
 
export class ParallelOrchestrator {
  async comparativeAnalysis(
    content: string,
    prompts: string[]
  ): Promise<Map<string, any>> {
    // TODO: Send same content to multiple models/prompts
    // Aggregate and compare results
    // Handle partial failures
  }
  
  async consensusDecision(
    question: string,
    context: string,
    models: string[]
  ): Promise<{
    consensus: string;
    confidence: number;
    dissent: string[];
  }> {
    // TODO: Get responses from multiple models
    // Find consensus
    // Calculate confidence score
  }
}

Exercise 3.3: Sequential Pipeline

// exercises/03-orchestration/pipeline.ts
// TODO: Build a multi-stage processing pipeline
 
interface PipelineStage {
  name: string;
  model: 'claude' | 'gemini';
  transform: (input: any) => Promise<any>;
}
 
export class ProcessingPipeline {
  private stages: PipelineStage[] = [];
  
  addStage(stage: PipelineStage): this {
    // TODO: Add stage to pipeline
    return this;
  }
  
  async execute(input: any): Promise<any> {
    // TODO: Execute stages sequentially
    // Pass output of each stage to next
    // Handle errors gracefully
  }
}
 
// Example usage:
// pipeline
//   .addStage({ name: 'load', model: 'gemini', transform: loadLargeDoc })
//   .addStage({ name: 'analyze', model: 'gemini', transform: analyzeStructure })
//   .addStage({ name: 'summarize', model: 'claude', transform: createSummary })

Success Criteria:

  • Router correctly assigns tasks based on characteristics
  • Parallel execution improves performance by >30%
  • Pipeline maintains context between stages

Exercise 4: Production Features

Objective: Add production-ready features including monitoring, error handling, and cost optimization.

Exercise 4.1: Comprehensive Error Handling

// exercises/04-production/error-handling.ts
// TODO: Implement production error handling
 
export class ProductionErrorHandler {
  async withRetry<T>(
    operation: () => Promise<T>,
    options?: {
      maxRetries?: number;
      backoff?: 'linear' | 'exponential';
      onRetry?: (attempt: number, error: Error) => void;
    }
  ): Promise<T> {
    // TODO: Implement retry logic with backoff
  }
  
  async withFallback<T>(
    primary: () => Promise<T>,
    fallbacks: Array<() => Promise<T>>
  ): Promise<T> {
    // TODO: Try primary, then fallbacks in order
  }
  
  async withCircuitBreaker<T>(
    operation: () => Promise<T>,
    breakerConfig: {
      threshold: number;
      timeout: number;
      resetTimeout: number;
    }
  ): Promise<T> {
    // TODO: Implement circuit breaker pattern
  }
}

Exercise 4.2: Performance Monitoring

// exercises/04-production/monitoring.ts
// TODO: Build a monitoring system
 
interface Metric {
  timestamp: Date;
  operation: string;
  duration: number;
  tokensIn: number;
  tokensOut: number;
  model: string;
  success: boolean;
  error?: string;
}
 
export class PerformanceMonitor {
  private metrics: Metric[] = [];
  
  async track<T>(
    operation: string,
    fn: () => Promise<T>,
    metadata?: Partial<Metric>
  ): Promise<T> {
    // TODO: Track operation performance
  }
  
  getStats(timeRange?: { start: Date; end: Date }) {
    // TODO: Calculate statistics
    // - Average latency by model
    // - Success rate
    // - Token usage trends
    // - Cost analysis
  }
  
  async exportMetrics(format: 'json' | 'csv' | 'prometheus') {
    // TODO: Export metrics in requested format
  }
}

Exercise 4.3: Cost Optimization

// exercises/04-production/cost-optimizer.ts
// TODO: Implement cost optimization strategies
 
interface ModelCosts {
  [model: string]: {
    inputCost: number; // per 1K tokens
    outputCost: number; // per 1K tokens
  };
}
 
export class CostOptimizer {
  constructor(private costs: ModelCosts, private budget: number) {}
  
  async optimizeRequest(
    content: string,
    requirements: {
      quality: 'high' | 'medium' | 'low';
      speed: 'fast' | 'normal' | 'slow';
      accuracy: number; // 0-1
    }
  ): Promise<{
    model: string;
    strategy: string;
    estimatedCost: number;
  }> {
    // TODO: Choose optimal model and strategy based on requirements
  }
  
  async trackSpending(operation: string, tokensIn: number, tokensOut: number, model: string) {
    // TODO: Track spending against budget
    // Alert when thresholds are reached
  }
}

Success Criteria:

  • Error handling prevents cascade failures
  • Monitoring captures all key metrics
  • Cost optimization reduces spending by >20%

Exercise 5: Advanced Integration

Objective: Build a complete application that showcases all learned concepts.

Exercise 5.1: Multi-Modal Analysis System

// exercises/05-advanced/multimodal.ts
// TODO: Build a system that can analyze code, docs, and images
 
export class MultiModalAnalyzer {
  async analyzeRepository(
    repoPath: string,
    options: {
      includeImages: boolean;
      includeDocs: boolean;
      includeCode: boolean;
    }
  ): Promise<{
    summary: string;
    insights: string[];
    recommendations: string[];
    visualizations: any[];
  }> {
    // TODO: Implement comprehensive analysis
    // 1. Load all content types
    // 2. Route to appropriate models
    // 3. Aggregate insights
    // 4. Generate visualizations
  }
}

Exercise 5.2: Intelligent Code Migration Assistant

// exercises/05-advanced/migration-assistant.ts
// TODO: Build an end-to-end migration assistant
 
export class MigrationAssistant {
  async planMigration(
    sourcePath: string,
    targetFramework: string,
    options?: {
      preserveStructure: boolean;
      modernize: boolean;
      testCoverage: number;
    }
  ): Promise<MigrationPlan> {
    // TODO: Complete implementation
    // Use Gemini for large context understanding
    // Use Claude for migration strategy
    // Generate step-by-step plan
  }
  
  async executeMigration(
    plan: MigrationPlan,
    options?: {
      dryRun: boolean;
      parallel: boolean;
      validateEachStep: boolean;
    }
  ): Promise<MigrationResult> {
    // TODO: Execute the migration plan
  }
}

Exercise 5.3: Real-time Collaboration System

// exercises/05-advanced/collaboration.ts
// TODO: Build a real-time collaborative analysis system
 
export class CollaborativeAnalyzer {
  private sessions = new Map<string, AnalysisSession>();
  
  async createSession(
    projectPath: string,
    participants: string[]
  ): Promise<string> {
    // TODO: Create collaborative session
  }
  
  async addAnalysis(
    sessionId: string,
    analysis: {
      type: string;
      content: string;
      author: string;
    }
  ): Promise<void> {
    // TODO: Add analysis to session
    // Trigger re-analysis if needed
    // Notify other participants
  }
  
  async generateReport(sessionId: string): Promise<Report> {
    // TODO: Aggregate all analyses
    // Use both Claude and Gemini
    // Create comprehensive report
  }
}

Success Criteria:

  • Can analyze mixed content types effectively
  • Migration plans are accurate and executable
  • Real-time collaboration works smoothly

Final Project

Objective: Design and build your own application using Claude + Gemini integration.

Requirements:

  1. Must use both Claude and Gemini appropriately
  2. Must handle at least 500K tokens of context
  3. Must include error handling and monitoring
  4. Must demonstrate cost optimization
  5. Must solve a real-world problem

Suggested Projects:

  • Documentation generator for large codebases
  • Security audit system for enterprise applications
  • Code review assistant with historical context
  • Technical debt analyzer and refactoring planner
  • Multi-repository dependency analyzer

Evaluation Criteria:

  • Architecture (25%): Clean, scalable design
  • Integration (25%): Effective use of both models
  • Performance (20%): Efficient processing of large contexts
  • Reliability (20%): Error handling and monitoring
  • Innovation (10%): Creative problem solving

Workshop Resources

Starter Code Repository

claude-gemini-workshop/
├── exercises/
│   ├── 01-basic-mcp/
│   ├── 02-large-context/
│   ├── 03-orchestration/
│   ├── 04-production/
│   └── 05-advanced/
├── solutions/
├── lib/
│   ├── test-helpers.ts
│   └── common-types.ts
├── tests/
├── package.json
└── README.md

Testing Your Solutions

# Test individual exercises
pnpm test:exercise-1.1
pnpm test:exercise-2.3
 
# Test all exercises in a section
pnpm test:section-3
 
# Run all tests
pnpm test
 
# Run with coverage
pnpm test:coverage

Debugging Tips

  1. Use Debug Mode:

    // Enable debug logging
    process.env.DEBUG = 'mcp:*,gemini:*';
  2. Monitor Token Usage:

    // Add to your code
    console.log(`Tokens used: ${estimateTokens(content)}`);
  3. Test with Smaller Contexts First:

    // Start small, then scale up
    const testContent = fullContent.slice(0, 1000);

Common Pitfalls to Avoid

  1. Not checking token limits before sending
  2. Forgetting to handle rate limits
  3. Not implementing proper error recovery
  4. Ignoring cost implications of large contexts
  5. Not caching expensive operations

Additional Challenges

Challenge 1: Streaming Optimization

Implement true streaming from Gemini through MCP to Claude Code client.

Challenge 2: Distributed Processing

Split large contexts across multiple Gemini instances and aggregate results.

Challenge 3: Smart Caching

Implement intelligent cache invalidation based on file changes.

Challenge 4: Cost Prediction

Build a system that accurately predicts costs before processing.

Challenge 5: Multi-Language Support

Extend the system to handle multiple programming languages with language-specific analysis.


Happy Coding! 🚀

For questions during the workshop, use the #workshop-help channel in our Discord.

Resources: