# Gemini + MCP 데모 스크립트 v7: 실제 동작 예제 및 테스트 ## 프로젝트 설정 ### 1. 디렉토리 구조 생성 ```bash mkdir gemini-mcp-demo cd gemini-mcp-demo npm init -y ``` ### 2. 의존성 설치 ```bash npm install @google/genai dotenv npm install -D typescript @types/node tsx ``` ### 3. 파일 생성 v6에서 작성한 코드를 각 파일로 생성: ``` gemini-mcp-demo/ ├── package.json ├── tsconfig.json ├── .env └── src/ ├── types.ts ├── mcp-adapter.ts ├── schema-converter.ts ├── gemini-mcp-client.ts └── index.ts ``` ### 4. 환경 변수 설정 `.env` 파일 생성: ```bash GEMINI_API_KEY=AIza... # Google AI Studio에서 발급 VERCEL_TOKEN=xxx... # Vercel Dashboard에서 발급 (선택사항) ``` ## 테스트 시나리오 ### 시나리오 1: 공개 도구 사용 (문서 검색) 인증 없이도 사용 가능한 공개 도구를 테스트한다. #### 코드 (examples/test-public-tool.ts) ```typescript import { GeminiMCPClient } from '../src/gemini-mcp-client.js'; import * as dotenv from 'dotenv'; dotenv.config(); async function testPublicTool() { const client = new GeminiMCPClient( process.env.GEMINI_API_KEY!, 'https://mcp.vercel.com' // Vercel token 없이 실행 ); await client.initialize(); const response = await client.chat( 'Vercel documentation에서 "edge functions"에 대해 검색하고 요약해줘' ); console.log('\n=== Final Response ==='); console.log(response); } testPublicTool().catch(console.error); ``` #### 예상 출력 ``` Fetching MCP tools... Found 12 MCP tools Gemini model initialized with MCP tools User: Vercel documentation에서 "edge functions"에 대해 검색하고 요약해줘 Gemini wants to call 1 function(s) Calling: search_docs Args: {"query":"edge functions"} Result: Edge Functions는 Vercel의 serverless 실행 환경으로, 전 세계 엣지 네트워크에서 코드를 실행합니다... Gemini: Edge Functions는 Vercel에서 제공하는 serverless 함수 실행 환경입니다. 주요 특징은 다음과 같습니다: 1. **글로벌 엣지 네트워크**: 사용자와 가장 가까운 위치에서 실행되어 지연 시간이 최소화됩니다. 2. **빠른 콜드 스타트**: 기존 serverless 함수보다 훨씬 빠르게 시작됩니다. 3. **제한된 런타임**: Node.js의 일부 API만 지원하며, 파일 시스템 접근 등이 제한됩니다. 4. **스트리밍 응답**: 실시간 스트리밍 응답을 지원합니다. 주로 A/B 테스팅, 개인화, 인증, 리다이렉션 등의 용도로 사용됩니다. ``` ### 시나리오 2: 인증 도구 사용 (프로젝트 조회) Vercel 계정이 필요한 인증 도구를 테스트한다. #### 코드 (examples/test-auth-tool.ts) ```typescript import { GeminiMCPClient } from '../src/gemini-mcp-client.js'; import * as dotenv from 'dotenv'; dotenv.config(); async function testAuthTool() { const client = new GeminiMCPClient( process.env.GEMINI_API_KEY!, 'https://mcp.vercel.com', process.env.VERCEL_TOKEN // 인증 필요 ); await client.initialize(); const response = await client.chat( '내 Vercel 프로젝트 목록을 보여주고, 각 프로젝트의 상태를 알려줘' ); console.log('\n=== Final Response ==='); console.log(response); } testAuthTool().catch(console.error); ``` #### 예상 출력 ``` Fetching MCP tools... Found 12 MCP tools Gemini model initialized with MCP tools User: 내 Vercel 프로젝트 목록을 보여주고, 각 프로젝트의 상태를 알려줘 Gemini wants to call 1 function(s) Calling: list_projects Args: {} Result: [{"id":"prj_abc123","name":"my-blog","framework":"nextjs",...}] Gemini: 현재 3개의 Vercel 프로젝트가 있습니다: 1. **my-blog** (Next.js) - 상태: Production Ready - 최근 배포: 2시간 전 - URL: my-blog.vercel.app 2. **api-server** (Node.js) - 상태: Production Ready - 최근 배포: 1일 전 - URL: api-server.vercel.app 3. **landing-page** (React) - 상태: Building - 진행 중: 새로운 배포가 빌드 중입니다 - URL: landing-page.vercel.app ``` ### 시나리오 3: 멀티턴 대화 (연속적인 질문) 여러 번의 대화를 이어가며 컨텍스트를 유지하는지 테스트한다. #### 코드 (examples/test-multiturn.ts) ```typescript import { GeminiMCPClient } from '../src/gemini-mcp-client.js'; import * as dotenv from 'dotenv'; dotenv.config(); async function testMultiTurn() { const client = new GeminiMCPClient( process.env.GEMINI_API_KEY!, 'https://mcp.vercel.com', process.env.VERCEL_TOKEN ); await client.initialize(); // 첫 번째 질문 console.log('\n=== Turn 1 ==='); const response1 = await client.chat( '내 프로젝트 중에 Next.js로 된 것을 찾아줘' ); // 두 번째 질문 (이전 컨텍스트 참조) console.log('\n=== Turn 2 ==='); const response2 = await client.chat( '그 프로젝트의 최근 배포 상태를 확인해줘' ); // 세 번째 질문 (더 구체적인 요청) console.log('\n=== Turn 3 ==='); const response3 = await client.chat( '배포 로그에 에러가 있는지 확인해줘' ); } testMultiTurn().catch(console.error); ``` #### 예상 출력 ``` === Turn 1 === User: 내 프로젝트 중에 Next.js로 된 것을 찾아줘 Gemini wants to call 1 function(s) Calling: list_projects Args: {} Result: [...] Gemini: Next.js 프레임워크를 사용하는 프로젝트는 2개입니다: 1. my-blog 2. e-commerce-site === Turn 2 === User: 그 프로젝트의 최근 배포 상태를 확인해줘 Gemini wants to call 2 function(s) Calling: get_deployments Args: {"projectId":"prj_abc123"} Result: [...] Calling: get_deployments Args: {"projectId":"prj_def456"} Result: [...] Gemini: 두 프로젝트의 최근 배포 상태입니다: **my-blog**: - 상태: Ready - 배포 시간: 2시간 전 - 빌드 시간: 1분 32초 **e-commerce-site**: - 상태: Error - 배포 시간: 5분 전 - 에러: Build failed === Turn 3 === User: 배포 로그에 에러가 있는지 확인해줘 Gemini wants to call 1 function(s) Calling: get_deployment_logs Args: {"projectId":"prj_def456","deploymentId":"dpl_xyz789"} Result: [... error logs ...] Gemini: e-commerce-site의 배포 로그에서 다음 에러를 발견했습니다: ``` Error: Module not found: Can't resolve '@/components/ProductCard' at compilation.js:123 ``` ProductCard 컴포넌트를 찾을 수 없다는 에러입니다. import 경로를 확인하거나 파일이 존재하는지 확인해보세요. ``` ## 성능 측정 ### 응답 시간 분석 간단한 성능 측정 코드: ```typescript // examples/measure-performance.ts async function measurePerformance() { const client = new GeminiMCPClient( process.env.GEMINI_API_KEY!, 'https://mcp.vercel.com' ); const startInit = Date.now(); await client.initialize(); const initTime = Date.now() - startInit; console.log(`Initialization: ${initTime}ms`); const startChat = Date.now(); await client.chat('Vercel docs에서 deployment 검색'); const chatTime = Date.now() - startChat; console.log(`Chat (with MCP call): ${chatTime}ms`); } ``` #### 예상 결과 ``` Initialization: 1,247ms - MCP tools fetch: 856ms - Gemini model init: 391ms Chat (with MCP call): 3,521ms - Gemini reasoning: 1,204ms - MCP tool call: 1,892ms - Gemini final response: 425ms ``` **관찰**: - MCP tools 조회는 초기 한 번만 수행 - MCP tool 호출이 전체 시간의 50% 이상 차지 - 네트워크 지연이 주요 병목 ## 트러블슈팅 ### 문제 1: 인증 오류 **증상**: ``` Error: MCP tools/call failed: 401 Unauthorized ``` **원인**: Vercel token이 없거나 만료됨 **해결**: ```bash # Vercel token 재발급 # https://vercel.com/account/tokens # .env 파일 업데이트 VERCEL_TOKEN=new_token_here ``` ### 문제 2: Gemini가 function을 호출하지 않음 **증상**: 사용자가 MCP tool이 필요한 질문을 했는데 Gemini가 직접 답변 **원인**: - Function description이 불명확 - 질문이 애매함 **해결**: ```typescript // 더 명확한 질문으로 변경 // Before await client.chat('Vercel 정보 알려줘'); // After await client.chat('내 Vercel 프로젝트 목록을 조회해줘'); ``` 또는 `FunctionCallingConfigMode.ANY` 사용: ```typescript this.model = this.genAI.getGenerativeModel({ model: 'gemini-2.0-flash-exp', tools: [{ functionDeclarations }], toolConfig: { functionCallingConfig: { mode: 'ANY' // 반드시 function 호출 } } }); ``` ### 문제 3: MCP 응답 파싱 오류 **증상**: ``` TypeError: Cannot read property 'text' of undefined ``` **원인**: MCP response format이 예상과 다름 **해결**: 더 견고한 파싱 로직 ```typescript // mcp-adapter.ts에서 개선 async callTool(name: string, args: Record): Promise { // ... fetch code ... const data: MCPToolCallResponse = await response.json(); if (data.error) { throw new Error(`MCP tool error: ${data.error.message}`); } // 안전한 파싱 if (!data.result) { return 'No result returned'; } if (!data.result.content) { return JSON.stringify(data.result); } const textContent = data.result.content .filter(c => c && c.type === 'text' && c.text) .map(c => c.text) .join('\n'); return textContent || JSON.stringify(data.result); } ``` ## 실전 팁 ### 1. 로깅 개선 디버깅을 위한 상세 로깅: ```typescript // 환경 변수로 제어 const VERBOSE = process.env.VERBOSE === 'true'; if (VERBOSE) { console.log('MCP Request:', JSON.stringify(request, null, 2)); console.log('MCP Response:', JSON.stringify(data, null, 2)); } ``` ### 2. 타임아웃 설정 ```typescript const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 30000); // 30초 try { const response = await fetch(this.endpoint, { signal: controller.signal, // ... other options }); } finally { clearTimeout(timeout); } ``` ### 3. 재시도 로직 ```typescript async function fetchWithRetry( fn: () => Promise, maxRetries = 3 ): Promise { for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (error: any) { if (i === maxRetries - 1) throw error; console.log(`Retry ${i + 1}/${maxRetries}...`); await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); } } throw new Error('Unreachable'); } ``` ## 성공 기준 이 데모가 성공적으로 작동한다는 것은: - ✅ Vercel MCP server에서 tools를 가져올 수 있다 - ✅ MCP tools를 Gemini function declarations로 변환할 수 있다 - ✅ Gemini가 적절한 function을 호출한다 - ✅ MCP tool 호출이 성공하고 결과를 반환한다 - ✅ Gemini가 결과를 이해하고 자연어로 응답한다 - ✅ 멀티턴 대화가 작동한다 ## 다음 단계 v8에서는 이 구현을 Claude Agent SDK와 비교하며, 각각의 장단점을 분석할 것이다. --- **작성일**: 2025-11-26 **테스트 환경**: Node.js 20+, TypeScript 5+ **예제 코드**: `examples/` 디렉토리 참조