Calling Gemini Models - 1M Context Window Integration
claude-code gemini ai-integration large-context typescript workshop
Overview
This guide demonstrates how to integrate Claude Code with Google’s Gemini models to leverage their industry-leading 1M+ context window capabilities. By combining Claude’s advanced reasoning with Gemini’s massive context processing, developers can build applications that analyze entire codebases, process lengthy documents, and maintain context across extended conversations.
Table of Contents
- Gemini Integration
- Gemini Integration
- Gemini Integration
- Gemini Integration
- Gemini Integration
- Gemini Integration
- Gemini Integration
- Gemini Integration
- Gemini Integration
Why Combine Claude and Gemini
Claude Code Strengths
- Superior reasoning and analysis capabilities
- Excellent code generation and understanding
- Strong safety and alignment features
- Native TypeScript SDK with streaming support
Gemini’s Context Window Advantages
- Gemini 2.5 Pro: 2 million token context window (largest available)
- Gemini 1.5 Flash: 1 million tokens with faster processing
- Process entire codebases (30,000+ lines)
- Analyze hours of video/audio content
- Maintain context across extensive documentation
Use Cases for Integration
- Codebase Analysis: Use Gemini to load entire repositories, Claude to perform complex reasoning
- Document Processing: Gemini handles large documents, Claude extracts insights
- Multi-file Refactoring: Gemini maintains context, Claude suggests improvements
- Long-form Content: Gemini processes books/transcripts, Claude summarizes
Architecture Overview
graph TD A[User Request] --> B[Claude Code] B --> C{Task Router} C -->|Large Context Task| D[MCP Server] D --> E[Gemini API] E --> F[Process with 1M Context] F --> G[Return to Claude] G --> H[Claude Analysis] H --> I[User Response] C -->|Standard Task| J[Claude Direct Processing] J --> I
Key Components
- Claude Code: Primary interface and orchestrator
- MCP Server: Bridge between Claude and external APIs
- Task Router: Determines optimal model for each task
- Gemini Integration: Handles large context processing
- Response Pipeline: Combines results from multiple models
Setting Up the Integration
Prerequisites
# Install required packages
npm install @anthropic-ai/claude-code @google/genai @modelcontextprotocol/sdk zod dotenvEnvironment Configuration
# .env file
CLAUDE_API_KEY=your_claude_api_key
GEMINI_API_KEY=your_gemini_api_key
GOOGLE_CLOUD_PROJECT=your_project_id
MCP_SERVER_PORT=3000Basic MCP Server Setup
// mcp-gemini-server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { GoogleGenerativeAI } from "@google/genai";
import { z } from "zod";
const gemini = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);
const server = new McpServer({
name: "gemini-integration",
version: "1.0.0",
description: "Gemini integration for large context processing"
});
// Tool for processing large contexts with Gemini
server.tool(
"process-large-context",
{
description: "Process large documents or codebases with Gemini's 1M+ context window",
inputSchema: z.object({
content: z.string().describe("The large content to process"),
prompt: z.string().describe("What to do with the content"),
model: z.enum(["gemini-1.5-flash", "gemini-1.5-pro", "gemini-2.5-pro"])
.default("gemini-1.5-flash")
})
},
async ({ content, prompt, model }) => {
const geminiModel = gemini.getGenerativeModel({ model });
const result = await geminiModel.generateContent([
{ text: content },
{ text: prompt }
]);
return {
content: [{
type: "text",
text: result.response.text()
}]
};
}
);
// Tool for streaming large context responses
server.tool(
"stream-large-context",
{
description: "Stream responses for large context processing",
inputSchema: z.object({
content: z.string(),
prompt: z.string(),
model: z.string().default("gemini-1.5-flash")
})
},
async ({ content, prompt, model }) => {
const geminiModel = gemini.getGenerativeModel({ model });
const result = await geminiModel.generateContentStream([
{ text: content },
{ text: prompt }
]);
let fullResponse = "";
for await (const chunk of result.stream) {
const chunkText = chunk.text();
fullResponse += chunkText;
// Send progress updates
server.notification({
method: "progress",
params: { text: chunkText }
});
}
return {
content: [{
type: "text",
text: fullResponse
}]
};
}
);
// Start the server
server.connect(process.stdin, process.stdout);Registering with Claude Code
# Add the MCP server to Claude Code
claude mcp add gemini-server -- node /path/to/mcp-gemini-server.js
# Or with TypeScript
claude mcp add gemini-server -- npx tsx /path/to/mcp-gemini-server.tsWorkshop Examples
Example 1: Analyzing an Entire Codebase
// workshop-example-1.ts
import { query, type SDKMessage } from "@anthropic-ai/claude-code";
async function analyzeCodebase(codebasePath: string) {
const messages: SDKMessage[] = [];
for await (const message of query({
prompt: `
I need to analyze a large codebase for architectural patterns and potential improvements.
First, use the process-large-context tool to load and understand the entire codebase at ${codebasePath}.
Then provide a detailed analysis of:
1. Overall architecture and design patterns
2. Code quality issues
3. Performance bottlenecks
4. Security concerns
5. Refactoring suggestions
`,
options: {
model: "claude-opus-4-20250514",
maxTurns: 5
}
})) {
messages.push(message);
if (message.type === "assistant" && message.content) {
console.log("Claude:", message.content);
}
}
return messages;
}
// Usage
analyzeCodebase("/path/to/large/repository");Example 2: Document Comparison with Context Preservation
// workshop-example-2.ts
interface DocumentComparison {
similarities: string[];
differences: string[];
recommendations: string[];
}
async function compareLargeDocuments(
doc1Path: string,
doc2Path: string
): Promise<DocumentComparison> {
const comparisonPrompt = `
You have access to two large documents that need to be compared.
1. First, use process-large-context to analyze the first document at ${doc1Path}
2. Then, use process-large-context to analyze the second document at ${doc2Path}
3. Compare them for:
- Key similarities in content and structure
- Major differences and contradictions
- Recommendations for reconciliation
Use Gemini's large context window to maintain full document context.
`;
let comparison: DocumentComparison = {
similarities: [],
differences: [],
recommendations: []
};
for await (const message of query({
prompt: comparisonPrompt,
options: {
systemPrompt: "You are an expert document analyst specializing in detailed comparisons."
}
})) {
if (message.type === "result" && message.result?.includes("similarities")) {
// Parse the structured response
comparison = JSON.parse(message.result);
}
}
return comparison;
}Example 3: Intelligent Model Routing
// workshop-example-3.ts
class IntelligentRouter {
private tokenCounter = {
estimateTokens: (text: string) => Math.ceil(text.length / 4)
};
async routeTask(content: string, task: string) {
const estimatedTokens = this.tokenCounter.estimateTokens(content);
// Decision logic for model routing
const routing = {
useGemini: estimatedTokens > 100_000, // Use Gemini for large contexts
model: estimatedTokens > 1_000_000 ? "gemini-2.5-pro" : "gemini-1.5-flash",
strategy: this.determineStrategy(task, estimatedTokens)
};
console.log(`Routing decision:`, routing);
const prompt = routing.useGemini
? `Use the process-large-context tool with model ${routing.model} to ${task}`
: `Directly process this task: ${task}`;
for await (const message of query({ prompt })) {
yield message;
}
}
private determineStrategy(task: string, tokens: number) {
if (task.includes("summarize") && tokens > 500_000) {
return "hierarchical-summarization";
} else if (task.includes("analyze") && tokens > 200_000) {
return "chunked-analysis";
} else if (task.includes("compare")) {
return "parallel-processing";
}
return "direct-processing";
}
}Example 4: Codebase Migration Assistant
// workshop-example-4.ts
interface MigrationPlan {
phases: Array<{
name: string;
files: string[];
changes: string[];
risks: string[];
}>;
timeline: string;
dependencies: Record<string, string[]>;
}
class CodebaseMigrationAssistant {
async planMigration(
sourcePath: string,
targetFramework: string
): Promise<MigrationPlan> {
const analysisPrompt = `
I need to plan a migration of a large codebase to ${targetFramework}.
Steps:
1. Use process-large-context to analyze the entire codebase at ${sourcePath}
2. Identify all files, dependencies, and architectural patterns
3. Create a detailed migration plan with:
- Phased approach (what to migrate first)
- File-by-file changes needed
- Risk assessment for each phase
- Dependency management strategy
- Estimated timeline
Output the plan as a structured JSON object.
`;
let plan: MigrationPlan | null = null;
for await (const message of query({
prompt: analysisPrompt,
options: {
model: "claude-opus-4-20250514",
systemPrompt: "You are an expert in codebase migrations and refactoring."
}
})) {
if (message.type === "assistant" && message.content?.includes("phases")) {
try {
plan = JSON.parse(message.content);
} catch (e) {
console.error("Failed to parse migration plan:", e);
}
}
}
return plan || this.getDefaultPlan();
}
private getDefaultPlan(): MigrationPlan {
return {
phases: [{
name: "Preparation",
files: [],
changes: ["Set up new project structure"],
risks: ["Incomplete analysis"]
}],
timeline: "Unknown",
dependencies: {}
};
}
}Example 5: Real-time Collaborative Analysis
// workshop-example-5.ts
import { EventEmitter } from 'events';
class CollaborativeAnalyzer extends EventEmitter {
private activeAnalyses = new Map<string, any>();
async analyzeWithProgress(
content: string,
analysisType: string,
onProgress?: (update: string) => void
) {
const analysisId = crypto.randomUUID();
this.activeAnalyses.set(analysisId, {
status: 'processing',
startTime: Date.now()
});
const streamingPrompt = `
Use the stream-large-context tool to analyze this content.
Provide regular progress updates as you work through the analysis.
Analysis type: ${analysisType}
Content size: ${content.length} characters
`;
try {
for await (const message of query({
prompt: streamingPrompt,
options: {
streamOptions: {
includeUsage: true
}
}
})) {
if (message.type === "assistant") {
onProgress?.(message.content || "Processing...");
this.emit('progress', {
analysisId,
content: message.content,
timestamp: Date.now()
});
}
if (message.type === "result") {
this.activeAnalyses.set(analysisId, {
status: 'completed',
result: message.result,
duration: Date.now() - this.activeAnalyses.get(analysisId).startTime
});
}
}
} catch (error) {
this.activeAnalyses.set(analysisId, {
status: 'error',
error: error.message
});
throw error;
}
return this.activeAnalyses.get(analysisId);
}
}
// Usage example
const analyzer = new CollaborativeAnalyzer();
analyzer.on('progress', (update) => {
console.log(`Analysis progress:`, update);
});
analyzer.analyzeWithProgress(
largeCodebaseContent,
"security-audit",
(progress) => {
console.log(`Update: ${progress}`);
}
);Production Patterns
Context Caching Strategy
// context-caching.ts
class GeminiContextCache {
private cache = new Map<string, {
content: string;
hash: string;
timestamp: number;
expiresAt: number;
}>();
private readonly CACHE_DURATION = 60 * 60 * 1000; // 1 hour
async processWithCache(
content: string,
prompt: string,
forceRefresh = false
) {
const contentHash = this.hashContent(content);
const cacheKey = `${contentHash}-${prompt}`;
if (!forceRefresh && this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey)!;
if (cached.expiresAt > Date.now()) {
console.log("Using cached context");
return cached.content;
}
}
// Process with Gemini
const result = await this.processWithGemini(content, prompt);
// Cache the result
this.cache.set(cacheKey, {
content: result,
hash: contentHash,
timestamp: Date.now(),
expiresAt: Date.now() + this.CACHE_DURATION
});
return result;
}
private hashContent(content: string): string {
// Simple hash for demo - use crypto.subtle.digest in production
return content.length + "-" + content.slice(0, 100);
}
private async processWithGemini(content: string, prompt: string) {
// Call to Gemini API
return "processed result";
}
}Error Handling and Fallbacks
// error-handling.ts
class ResilientGeminiClient {
private readonly fallbackStrategies = {
tokenLimit: async (content: string, prompt: string) => {
// Fallback to chunking if content exceeds limits
const chunks = this.chunkContent(content, 500_000);
const results = await Promise.all(
chunks.map(chunk => this.processChunk(chunk, prompt))
);
return this.mergeResults(results);
},
rateLimit: async (content: string, prompt: string) => {
// Switch to Gemini Flash for rate limit issues
return this.processWithModel(content, prompt, "gemini-1.5-flash");
},
timeout: async (content: string, prompt: string) => {
// Simplify the prompt for faster processing
const simplifiedPrompt = `Briefly: ${prompt}`;
return this.processWithTimeout(content, simplifiedPrompt, 60000);
}
};
async process(content: string, prompt: string) {
try {
return await this.processWithGemini(content, prompt);
} catch (error) {
return this.handleError(error, content, prompt);
}
}
private async handleError(
error: any,
content: string,
prompt: string
) {
if (error.code === 'RESOURCE_EXHAUSTED') {
return this.fallbackStrategies.rateLimit(content, prompt);
} else if (error.message?.includes('token limit')) {
return this.fallbackStrategies.tokenLimit(content, prompt);
} else if (error.code === 'DEADLINE_EXCEEDED') {
return this.fallbackStrategies.timeout(content, prompt);
}
throw error; // Re-throw if no fallback applies
}
private chunkContent(content: string, maxSize: number): string[] {
const chunks: string[] = [];
for (let i = 0; i < content.length; i += maxSize) {
chunks.push(content.slice(i, i + maxSize));
}
return chunks;
}
private mergeResults(results: string[]): string {
return results.join("\n\n---\n\n");
}
private async processWithGemini(content: string, prompt: string) {
// Actual Gemini API call
throw new Error("Not implemented");
}
private async processChunk(chunk: string, prompt: string) {
return `Processed: ${chunk.slice(0, 50)}...`;
}
private async processWithModel(
content: string,
prompt: string,
model: string
) {
return `Processed with ${model}`;
}
private async processWithTimeout(
content: string,
prompt: string,
timeout: number
) {
return `Processed with timeout: ${timeout}ms`;
}
}Performance Monitoring
// performance-monitoring.ts
interface PerformanceMetrics {
modelUsed: string;
inputTokens: number;
outputTokens: number;
latency: number;
cacheHit: boolean;
cost: number;
}
class PerformanceMonitor {
private metrics: PerformanceMetrics[] = [];
async trackOperation<T>(
operation: () => Promise<T>,
metadata: Partial<PerformanceMetrics>
): Promise<T> {
const startTime = Date.now();
try {
const result = await operation();
const metrics: PerformanceMetrics = {
modelUsed: metadata.modelUsed || "unknown",
inputTokens: metadata.inputTokens || 0,
outputTokens: metadata.outputTokens || 0,
latency: Date.now() - startTime,
cacheHit: metadata.cacheHit || false,
cost: this.calculateCost(metadata)
};
this.metrics.push(metrics);
this.logMetrics(metrics);
return result;
} catch (error) {
this.logError(error, Date.now() - startTime);
throw error;
}
}
private calculateCost(metadata: Partial<PerformanceMetrics>): number {
const rates = {
"gemini-1.5-flash": { input: 0.000001, output: 0.000002 },
"gemini-1.5-pro": { input: 0.00001, output: 0.00002 },
"gemini-2.5-pro": { input: 0.000011, output: 0.000022 }
};
const model = metadata.modelUsed || "gemini-1.5-flash";
const rate = rates[model] || rates["gemini-1.5-flash"];
return (metadata.inputTokens || 0) * rate.input +
(metadata.outputTokens || 0) * rate.output;
}
private logMetrics(metrics: PerformanceMetrics) {
console.log("Performance metrics:", {
...metrics,
cost: `$${metrics.cost.toFixed(6)}`
});
}
private logError(error: any, duration: number) {
console.error("Operation failed:", {
error: error.message,
duration,
timestamp: new Date().toISOString()
});
}
getAggregateMetrics() {
return {
totalOperations: this.metrics.length,
averageLatency: this.metrics.reduce((sum, m) => sum + m.latency, 0) / this.metrics.length,
totalCost: this.metrics.reduce((sum, m) => sum + m.cost, 0),
cacheHitRate: this.metrics.filter(m => m.cacheHit).length / this.metrics.length
};
}
}Performance Optimization
Token Usage Optimization
// token-optimization.ts
class TokenOptimizer {
private readonly strategies = {
compression: {
whitespace: (text: string) => text.replace(/\s+/g, ' ').trim(),
comments: (text: string) => text.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, ''),
emptyLines: (text: string) => text.replace(/\n\s*\n/g, '\n')
},
filtering: {
byExtension: (files: any[], extensions: string[]) =>
files.filter(f => extensions.includes(f.extension)),
bySize: (files: any[], maxSize: number) =>
files.filter(f => f.size <= maxSize),
byRelevance: (files: any[], query: string) =>
files.filter(f => f.content.includes(query))
}
};
optimizeForContext(content: string, targetTokens: number): string {
let optimized = content;
// Apply compression strategies
for (const [name, strategy] of Object.entries(this.strategies.compression)) {
optimized = strategy(optimized);
if (this.estimateTokens(optimized) <= targetTokens) {
return optimized;
}
}
// If still too large, truncate intelligently
return this.intelligentTruncate(optimized, targetTokens);
}
private estimateTokens(text: string): number {
// Rough estimation: 1 token ≈ 4 characters
return Math.ceil(text.length / 4);
}
private intelligentTruncate(text: string, targetTokens: number): string {
const targetChars = targetTokens * 4;
if (text.length <= targetChars) {
return text;
}
// Try to truncate at a natural boundary
const truncated = text.slice(0, targetChars);
const lastNewline = truncated.lastIndexOf('\n');
if (lastNewline > targetChars * 0.8) {
return truncated.slice(0, lastNewline);
}
return truncated + "\n... [truncated]";
}
}Parallel Processing
// parallel-processing.ts
class ParallelProcessor {
private readonly MAX_CONCURRENT = 5;
async processLargeDataset<T>(
items: T[],
processor: (item: T) => Promise<any>
): Promise<any[]> {
const results: any[] = [];
const queue = [...items];
const inProgress = new Set<Promise<any>>();
while (queue.length > 0 || inProgress.size > 0) {
// Start new tasks up to the limit
while (inProgress.size < this.MAX_CONCURRENT && queue.length > 0) {
const item = queue.shift()!;
const task = this.processWithRetry(item, processor)
.then(result => {
results.push(result);
inProgress.delete(task);
})
.catch(error => {
console.error("Task failed:", error);
inProgress.delete(task);
});
inProgress.add(task);
}
// Wait for at least one task to complete
if (inProgress.size > 0) {
await Promise.race(inProgress);
}
}
return results;
}
private async processWithRetry<T>(
item: T,
processor: (item: T) => Promise<any>,
maxRetries = 3
): Promise<any> {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await processor(item);
} catch (error) {
lastError = error;
await this.delay(Math.pow(2, attempt) * 1000);
}
}
throw lastError;
}
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}Cost Management
Cost Tracking and Budgeting
// cost-management.ts
class CostManager {
private budget: number;
private spent: number = 0;
private alerts: Array<{ threshold: number; callback: () => void }> = [];
constructor(budget: number) {
this.budget = budget;
}
async trackOperation<T>(
operation: () => Promise<T>,
estimatedCost: number
): Promise<T> {
if (this.spent + estimatedCost > this.budget) {
throw new Error(`Operation would exceed budget. Current: $${this.spent}, Estimated: $${estimatedCost}, Budget: $${this.budget}`);
}
const result = await operation();
this.spent += estimatedCost;
this.checkAlerts();
return result;
}
addAlert(threshold: number, callback: () => void) {
this.alerts.push({ threshold, callback });
}
private checkAlerts() {
const percentSpent = (this.spent / this.budget) * 100;
for (const alert of this.alerts) {
if (percentSpent >= alert.threshold) {
alert.callback();
}
}
}
getStatus() {
return {
budget: this.budget,
spent: this.spent,
remaining: this.budget - this.spent,
percentUsed: (this.spent / this.budget) * 100
};
}
}
// Usage
const costManager = new CostManager(100); // $100 budget
costManager.addAlert(80, () => {
console.warn("80% of budget consumed!");
});
await costManager.trackOperation(
async () => processWithGemini(largeContent),
0.50 // Estimated $0.50 cost
);Troubleshooting
Common Issues and Solutions
-
Token Limit Exceeded
// Solution: Implement chunking strategy if (error.message.includes("token limit")) { const chunks = chunkContent(content, 500000); const results = await Promise.all( chunks.map(chunk => processChunk(chunk)) ); return mergeResults(results); } -
Rate Limiting
// Solution: Implement exponential backoff async function withRateLimitRetry(fn: () => Promise<any>) { for (let i = 0; i < 5; i++) { try { return await fn(); } catch (error) { if (error.status === 429) { await delay(Math.pow(2, i) * 1000); continue; } throw error; } } } -
Context Cache Misses
// Solution: Implement cache warming async function warmCache(documents: string[]) { const warmupPromises = documents.map(doc => processWithCache(doc, "analyze", { warmup: true }) ); await Promise.all(warmupPromises); }
Debugging Techniques
// debug-helper.ts
class DebugHelper {
static logContextSize(content: string) {
console.log({
characters: content.length,
estimatedTokens: Math.ceil(content.length / 4),
lines: content.split('\n').length,
recommendedModel: content.length > 4_000_000 ? 'gemini-2.5-pro' : 'gemini-1.5-flash'
});
}
static async measurePerformance(
operation: () => Promise<any>,
label: string
) {
console.time(label);
const memBefore = process.memoryUsage();
try {
const result = await operation();
console.timeEnd(label);
const memAfter = process.memoryUsage();
console.log(`Memory delta: ${(memAfter.heapUsed - memBefore.heapUsed) / 1024 / 1024}MB`);
return result;
} catch (error) {
console.timeEnd(label);
throw error;
}
}
}Additional Resources
Related Documentation
External Links
Workshop Materials
- Workshop materials available upon request
- Example implementations in the patterns library
- Interactive exercises coming soon
Community Resources
Tags: claude-code gemini ai-integration large-context typescript workshop mcp model-orchestration production-patterns
Version: 1.0.0