반응형
LangChain으로 Gemini 붙이려다 Firebase 생태계랑 안 맞아서 결국 두 배 코드 짠 적 있으시죠. Genkit은 Firebase Functions, Firestore, Cloud Trace를 네이티브로 연결하면서 MCP 서버까지 한 줄로 붙입니다.
[핵심 요약]
→ Genkit: AI 앱 개발용 Google 공식 오픈소스 프레임워크 (JS/TS, Go, Python)
→ MCP 통합: createMcpHost / createMcpClient로 외부 MCP 서버 툴 자동 등록
→ 양방향 지원: MCP 클라이언트(외부 툴 소비) + MCP 서버(내 툴 외부 노출) 모두 가능
→ Firebase Functions 위에서 에이전트 플로우 실행 → Cloud Trace 자동 추적
→ 모델 무관: Gemini, Claude, OpenAI, Ollama, DeepSeek 전부 지원
→ 툴 자동 네임스페이스: fs/read_file, memory/store처럼 서버명으로 자동 분리
Genkit이 LangChain 대신인 이유
LangChain + Firebase:
→ Firebase Functions에 LangChain 올리면 콜드스타트 느림
→ Firestore 연동 별도 어댑터 필요
→ Cloud Trace 연동 수동 설정
→ Gemini 모델 업데이트 추적 직접 관리
Genkit + Firebase:
→ Firebase Functions 네이티브 지원 (onCallGenkit 한 줄)
→ Firestore retriever 내장
→ Cloud Trace 자동 연동 (플로우 전 구간 추적)
→ Google AI 모델 플러그인 공식 관리
→ Gemini CLI, Claude Code에서 MCP로 직접 연결 가능
실전 1 — 설치 및 기본 세팅
# 프로젝트 초기화
mkdir genkit-mcp-app && cd genkit-mcp-app
npm init -y
npm install typescript @types/node --save-dev
# Genkit 핵심 패키지
npm install genkit @genkit-ai/google-genai
# MCP 플러그인 (두 가지 중 선택)
npm install @genkit-ai/mcp # 공식 패키지 (최신)
# 또는
npm install genkitx-mcp # 커뮤니티 패키지 (레거시)
# Firebase Functions 함께 쓸 경우
npm install firebase-functions firebase-admin
// src/index.ts — 기본 Genkit 세팅
import { genkit } from 'genkit';
import { googleAI } from '@genkit-ai/google-genai';
export const ai = genkit({
plugins: [
googleAI({ apiKey: process.env.GOOGLE_GENAI_API_KEY }),
],
model: 'googleai/gemini-2.5-flash', // 기본 모델
});
[Genkit 지원 모델 플러그인]
→ @genkit-ai/google-genai: Gemini 전 시리즈
→ @genkit-ai/anthropic: Claude (Vertex AI 경유)
→ @genkit-ai/ollama: Gemma, Llama, DeepSeek 로컬 실행
→ @genkit-ai/openai: GPT 시리즈
→ 모델 전환: model 파라미터 하나만 바꾸면 됨
실전 2 — MCP 클라이언트: 외부 툴 가져오기
MCP 서버의 툴을 Genkit 에이전트에서 바로 씁니다. 툴은 서버명으로 자동 네임스페이싱됩니다.
단일 서버 연결 (createMcpClient)
import { genkit } from 'genkit';
import { googleAI } from '@genkit-ai/google-genai';
import { createMcpClient } from '@genkit-ai/mcp';
// Filesystem MCP 서버 연결
const fsClient = createMcpClient({
name: 'fs', // 네임스페이스: fs/read_file, fs/write_file ...
mcpServer: {
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()],
},
});
const ai = genkit({
plugins: [googleAI()],
});
(async () => {
await fsClient.ready(); // 서버 연결 대기
// MCP 툴 자동 탐색 후 모델에 주입
const tools = await fsClient.getActiveTools(ai);
const { text } = await ai.generate({
model: 'googleai/gemini-2.5-flash',
prompt: '현재 디렉토리의 파일 목록 보여줘',
tools, // fs/list_directory, fs/read_file 등 자동 포함
});
console.log(text);
await fsClient.disable();
})();
다중 서버 연결 (createMcpHost)
import { createMcpHost } from '@genkit-ai/mcp';
// 여러 MCP 서버 동시 연결
const mcpHost = createMcpHost({
name: 'myAgentTools',
mcpServers: {
// 키 이름이 네임스페이스
fs: {
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()],
},
memory: {
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-memory'],
},
github: {
url: 'https://api.githubcopilot.com/mcp/', // 원격 MCP 서버
env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN! },
},
},
});
const ai = genkit({ plugins: [googleAI()] });
(async () => {
const { text } = await ai.generate({
model: 'googleai/gemini-2.5-flash',
prompt: 'README.md 읽고 GitHub 이슈로 등록해줘',
tools: await mcpHost.getActiveTools(ai),
// → fs/read_file, memory/store, github/create_issue 전부 사용 가능
});
console.log(text);
await mcpHost.close();
})();
[createMcpClient vs createMcpHost]
→ createMcpClient: 서버 1개 연결, 단순 케이스
→ createMcpHost: 서버 N개 동시 관리, 런타임 연결/해제 지원
→ 네임스페이스: fs/read_file, github/create_issue처럼 서버명 자동 prefix
→ 원격 서버: url 옵션으로 HTTP MCP 서버 연결 가능
→ 로컬 서버: command + args로 subprocess 실행
실전 3 — MCP 서버: 내 Genkit 툴을 외부에 노출
Firestore 데이터 조회, 내부 API 호출 같은 툴을 MCP 서버로 노출합니다. Gemini CLI, Claude Code, Cursor에서 바로 붙여 쓸 수 있습니다.
import { genkit, z } from 'genkit';
import { googleAI } from '@genkit-ai/google-genai';
import { mcpServer } from '@genkit-ai/mcp';
import * as admin from 'firebase-admin';
admin.initializeApp();
const db = admin.firestore();
const ai = genkit({ plugins: [googleAI()] });
// Firestore 조회 툴 정의
ai.defineTool(
{
name: 'getOrder',
description: '주문 ID로 주문 정보 조회',
inputSchema: z.object({
orderId: z.string().describe('조회할 주문 ID'),
}),
outputSchema: z.object({
orderId: z.string(),
status: z.string(),
amount: z.number(),
createdAt: z.string(),
}).optional(),
},
async ({ orderId }) => {
const doc = await db.collection('orders').doc(orderId).get();
if (!doc.exists) return undefined;
return { orderId: doc.id, ...doc.data() } as any;
}
);
// 주문 상태 업데이트 툴
ai.defineTool(
{
name: 'updateOrderStatus',
description: '주문 상태 업데이트',
inputSchema: z.object({
orderId: z.string(),
status: z.enum(['pending', 'processing', 'shipped', 'delivered']),
}),
outputSchema: z.boolean(),
},
async ({ orderId, status }) => {
await db.collection('orders').doc(orderId).update({ status });
return true;
}
);
// ✅ Genkit 앱을 MCP 서버로 노출
mcpServer(ai, {
name: 'order-management',
version: '1.0.0',
}).start();
// → getOrder, updateOrderStatus 툴이 MCP 표준으로 노출됨
이제 Claude Code나 Gemini CLI에서 이 서버에 바로 붙을 수 있습니다.
# Claude Code에서 연결
claude mcp add-json "orders" \
'{"command":"node","args":["dist/index.js"]}'
# Gemini CLI에서 연결
# .gemini/settings.json
{
"mcpServers": {
"orders": {
"command": "node",
"args": ["dist/index.js"]
}
}
}
[MCP 서버 노출 시 주의점]
→ inputSchema는 객체 타입만 가능 (MCP 스펙 제한)
→ 복잡한 중첩 객체는 JSON 직렬화해서 string으로 전달
→ 원격 노출 시 HTTPS + API 키 인증 필수 (MCP 자체 보안 없음)
→ 버전 관리: mcpServer version 필드로 브레이킹 체인지 관리
실전 4 — Firebase Functions 배포 + Cloud Trace
에이전트 플로우를 Firebase Functions 위에 올리고 Cloud Trace로 전 구간 추적합니다.
import { genkit, z } from 'genkit';
import { googleAI } from '@genkit-ai/google-genai';
import { createMcpHost } from '@genkit-ai/mcp';
import { onCallGenkit } from 'firebase-functions/https';
import { defineSecret } from 'firebase-functions/params';
// Secret Manager에서 API 키 로드
const geminiKey = defineSecret('GOOGLE_GENAI_API_KEY');
const githubToken = defineSecret('GITHUB_TOKEN');
const mcpHost = createMcpHost({
name: 'agentTools',
mcpServers: {
github: {
url: 'https://api.githubcopilot.com/mcp/',
env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN! },
},
},
});
const ai = genkit({
plugins: [googleAI()],
});
// 에이전트 플로우 정의
const codeReviewFlow = ai.defineFlow(
{
name: 'codeReviewFlow',
inputSchema: z.object({
prUrl: z.string(),
focusArea: z.string().optional(),
}),
outputSchema: z.object({
review: z.string(),
suggestions: z.array(z.string()),
}),
},
async ({ prUrl, focusArea }) => {
const tools = await mcpHost.getActiveTools(ai);
const { output } = await ai.generate({
model: 'googleai/gemini-2.5-flash',
prompt: `PR ${prUrl}을 리뷰해줘. ${focusArea ? `특히 ${focusArea} 위주로.` : ''}`,
tools,
output: {
schema: z.object({
review: z.string(),
suggestions: z.array(z.string()),
}),
},
});
return output!;
}
);
// ✅ Firebase Functions로 배포
export const codeReview = onCallGenkit(
{
secrets: [geminiKey, githubToken],
region: 'asia-northeast3', // 서울
cors: true,
},
codeReviewFlow
);
[Firebase Functions + Genkit 핵심]
→ onCallGenkit: 플로우를 Callable Function으로 한 줄 배포
→ Secret Manager: defineSecret으로 API 키 안전 관리
→ Cloud Trace: 플로우 실행 전 구간 자동 추적 (별도 설정 불필요)
→ Firebase Console → Genkit → Monitoring에서 트레이스 조회
→ 리전: asia-northeast3 (서울) 선택 시 레이턴시 최소화
마무리
✅ Genkit + MCP 써야 하는 경우
→ Firebase 생태계(Firestore, Auth, Functions) 메인으로 쓰는 팀
→ Gemini 모델 기반 앱에 외부 MCP 툴 붙이고 싶을 때
→ 내부 Firestore/API 툴을 Claude Code나 Gemini CLI에 노출하고 싶을 때
→ Cloud Trace로 에이전트 실행 흐름 추적이 필요한 프로덕션 환경
→ Flutter/Android 앱 백엔드에 AI 에이전트 붙이는 경우
❌ Genkit이 맞지 않는 경우
→ AWS나 Azure 생태계 메인인 팀 (Firebase 연동 이점 없음)
→ LangGraph처럼 복잡한 상태 머신 기반 에이전트가 필요한 경우
→ Python 메인 팀 (Python SDK는 아직 알파)
→ Anthropic/OpenAI API만 쓰고 Google 생태계 아예 안 쓰는 팀
반응형
'AI Agent' 카테고리의 다른 글
| PydanticAI 완전 가이드 — LLM 출력을 문자열 말고 타입으로 받는 법 (0) | 2026.05.18 |
|---|---|
| Mastra AI 완전 가이드 — TypeScript로 AI 에이전트 만드는 LangChain 대안 (0) | 2026.05.18 |
| LangGraph 상태 영속성(Checkpointing) — 에이전트가 죽어도 이어서 실행하는 법 (0) | 2026.05.18 |
| 5개국 "에이전트 AI 보안 가이드" 완전 분석 — 정부가 경고한 AI 에이전트 5가지 위험과 개발자 체크리스트 (0) | 2026.05.06 |
| 임베딩 모델 완전 가이드 — text-embedding 선택과 RAG 적용 (0) | 2026.05.04 |