Dart와 Flutter에서 대규모 언어 모델(LLM)을 활용하는 가장 강력한 패키지, mcp_llm을 소개합니다. 이 글은 MCP 시리즈의 세 번째 포스트로, MCP 서버 구현하기와 MCP 클라이언트 구현하기에 이어 MCP 생태계의 핵심 시리즈를 완성합니다.
Model Context Protocol (MCP)은 AI 모델과 외부 환경 간의 통신을 표준화하는 프로토콜입니다. 이전 포스트에서 소개했던 mcp_client와 mcp_server가 이 프로토콜의 클라이언트/서버 구현을 제공한다면, 오늘 소개할 mcp_llm은 대규모 언어 모델(LLM)을 MCP 생태계와 통합하는 패키지입니다.
MCP의 주요 목표는 AI 모델이 외부 환경의 도구, 리소스, 프롬프트 등에 접근할 수 있게 하는 것입니다. mcp_llm은 이 목표를 한 단계 더 발전시켜, 다양한 LLM 제공자(Claude, OpenAI, Together AI 등)를 통합하고, 이들의 기능을 Flutter 애플리케이션에서 활용할 수 있도록 합니다.
mcp_llm 패키지는 Dart와 Flutter 환경에서 다양한 LLM 제공자를 통합하고, MCP 프로토콜을 활용하여 확장 가능한 AI 솔루션을 구축할 수 있는 강력한 도구입니다.
mcp_llm은 다음과 같은 다양한 사용 사례에 적합합니다:
mcp_llm의 핵심 아키텍처는 다음 주요 컴포넌트로 구성됩니다:
메인 클래스로, 전체 패키지의 진입점입니다. 프로바이더 등록, 클라이언트/서버 생성, 플러그인 관리 등 대부분의 주요 기능에 접근할 수 있습니다.
// McpLlm 인스턴스 생성
final mcpLlm = McpLlm();
// 프로바이더 등록
mcpLlm.registerProvider('claude', ClaudeProviderFactory());
mcpLlm.registerProvider('openai', OpenAiProviderFactory());
클라이언트 측 LLM 기능을 제공하며, mcp_client와 통합됩니다. AI 모델에 쿼리를 보내고, 응답을 받으며, 도구 호출을 처리합니다.
// LlmClient 생성
final client = await mcpLlm.createClient(
providerName: 'claude',
config: LlmConfiguration(
apiKey: 'your-api-key',
model: 'claude-3-haiku-20240307',
),
);
// AI와 채팅
final response = await client.chat("What's the weather like today?");
서버 측 LLM 기능을 제공하며, mcp_server와 통합됩니다. AI 기능을 서비스로 제공하고, 외부 클라이언트의 요청을 처리합니다.
// LlmServer 생성
final server = await mcpLlm.createServer(
providerName: 'openai',
config: LlmConfiguration(
apiKey: 'your-api-key',
model: 'gpt-4',
),
);
// 로컬 도구 등록
server.registerLocalTool(
name: 'calculator',
description: 'Performs calculations',
inputSchema: {...},
handler: calculatorHandler,
);
다양한 LLM API와 통신하는 구현체입니다. 각 제공자는 LlmInterface를 구현하며, 특정 LLM 서비스와의 통신을 담당합니다.
// 지원되는 제공자들
mcpLlm.registerProvider('claude', ClaudeProviderFactory());
mcpLlm.registerProvider('openai', OpenAiProviderFactory());
mcpLlm.registerProvider('together', TogetherProviderFactory());
확장 기능을 제공하는 플러그인 시스템으로, 도구, 프롬프트, 리소스 등을 등록하고 관리합니다.
// 플러그인 등록
await mcpLlm.registerPlugin(myToolPlugin);
문서 저장, 임베딩 관리, 벡터 검색 등 Retrieval Augmented Generation 기능을 제공합니다.
// 검색 관리자 생성
final retrievalManager = mcpLlm.createRetrievalManager(
providerName: 'openai',
documentStore: documentStore,
);
프로젝트의 pubspec.yaml 파일에 mcp_llm 종속성을 추가합니다:
dependencies:
mcp_llm: ^0.2.2
flutter:
sdk: flutter
또는 명령줄에서 다음과 같이 실행합니다:
flutter pub add mcp_llm
사용할 LLM 제공자에 따라 API 키를 준비해야 합니다. 다음은 주요 제공자의 API 키 설정 방법입니다:
// API 키 설정 방법
final claudeConfig = LlmConfiguration(
apiKey: 'your-claude-api-key',
model: 'claude-3-haiku-20240307',
);
final openAiConfig = LlmConfiguration(
apiKey: 'your-openai-api-key',
model: 'gpt-4',
);
final togetherConfig = LlmConfiguration(
apiKey: 'your-together-api-key',
model: 'llama-3',
);
API 키는 보안을 위해 환경 변수나 보안 저장소에서 불러오는 것이 좋습니다:
// 환경 변수에서 API 키 로드
final apiKey = Platform.environment['CLAUDE_API_KEY'] ??
await secureStorage.read(key: 'claude_api_key');
McpLlm 인스턴스를 생성하고 사용할 LLM 제공자를 등록합니다:
import 'package:mcp_llm/mcp_llm.dart';
void main() async {
// McpLlm 인스턴스 생성
final mcpLlm = McpLlm();
// 로깅 설정 (선택 사항)
final logger = Logger.getLogger('mcp_llm.main');
logger.setLevel(LogLevel.debug);
// 프로바이더 등록
mcpLlm.registerProvider('claude', ClaudeProviderFactory());
mcpLlm.registerProvider('openai', OpenAiProviderFactory());
// 사용 가능한 프로바이더 목록 및 기능 확인
final capabilities = mcpLlm.getProviderCapabilities();
logger.info('Available providers: ${capabilities.keys.join(', ')}');
// 자원 정리
await mcpLlm.shutdown();
}
AI 모델과 통신하기 위한 클라이언트를 생성합니다:
// LlmClient 생성
final client = await mcpLlm.createClient(
providerName: 'claude',
config: LlmConfiguration(
apiKey: 'your-claude-api-key',
model: 'claude-3-haiku-20240307',
options: {
'temperature': 0.7,
'max_tokens': 1500,
},
),
systemPrompt: 'You are a helpful assistant specialized in Flutter development.',
);
// 채팅
final response = await client.chat(
"What's the best state management solution for Flutter?",
);
print('AI Response: ${response.text}');
실시간 응답을 스트리밍으로 받아 처리할 수 있습니다:
// 스트리밍 응답
final responseStream = client.streamChat(
"Explain how Flutter's widget tree works",
);
// 응답 청크 처리
await for (final chunk in responseStream) {
// 응답 청크 처리
print('Chunk: ${chunk.textChunk}');
// 완료 여부 확인
if (chunk.isDone) {
print('Response completed');
break;
}
}
AI 기능을 제공하는 서버를 생성합니다:
// LlmServer 생성
final server = await mcpLlm.createServer(
providerName: 'openai',
config: LlmConfiguration(
apiKey: 'your-openai-api-key',
model: 'gpt-4',
),
);
// 로컬 도구 등록
server.registerLocalTool(
name: 'calculator',
description: 'Performs basic arithmetic operations',
inputSchema: {
'type': 'object',
'properties': {
'operation': {
'type': 'string',
'enum': ['add', 'subtract', 'multiply', 'divide'],
},
'a': {'type': 'number'},
'b': {'type': 'number'},
},
'required': ['operation', 'a', 'b'],
},
handler: (args) async {
final operation = args['operation'] as String;
final a = args['a'] as num;
final b = args['b'] as num;
switch (operation) {
case 'add': return {'result': a + b};
case 'subtract': return {'result': a - b};
case 'multiply': return {'result': a * b};
case 'divide': return {'result': a / b};
default: throw ArgumentError('Unknown operation: $operation');
}
},
);
// 쿼리 처리
final result = await server.processQuery(
query: "What's 25 + 17?",
useLocalTools: true,
);
print('AI Response: ${result.text}');
이제 mcp_llm을 사용하여 간단한 AI 채팅 앱을 만들어 보겠습니다. 이 예제는 Flutter를 사용하여 기본적인 채팅 인터페이스를 구현하고, Claude API를 통해 AI 응답을 생성합니다.
새로운 Flutter 프로젝트를 생성하고 필요한 종속성을 추가합니다:
flutter create ai_chat_app
cd ai_chat_app
flutter pub add mcp_llm
API 키를 안전하게 관리하기 위해 .env 파일과 flutter_dotenv 패키지를 사용합니다:
flutter pub add flutter_dotenv
프로젝트 루트에 .env 파일을 생성합니다:
CLAUDE_API_KEY=your-claude-api-key
pubspec.yaml에 환경 파일을 에셋으로 추가합니다:
flutter:
assets:
- .env
이제 lib/main.dart 파일을 다음과 같이 수정합니다:
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:mcp_llm/mcp_llm.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'AI Chat App',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const ChatScreen(),
);
}
}
class ChatScreen extends StatefulWidget {
const ChatScreen({super.key});
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final TextEditingController _textController = TextEditingController();
final List<ChatMessage> _messages = [];
late McpLlm _mcpLlm;
LlmClient? _client;
bool _isTyping = false;
void initState() {
super.initState();
_initializeLlm();
}
Future<void> _initializeLlm() async {
_mcpLlm = McpLlm();
_mcpLlm.registerProvider('claude', ClaudeProviderFactory());
final apiKey = dotenv.env['CLAUDE_API_KEY'] ?? '';
if (apiKey.isEmpty) {
_showError('API key not found. Please check your .env file.');
return;
}
try {
_client = await _mcpLlm.createClient(
providerName: 'claude',
config: LlmConfiguration(
apiKey: apiKey,
model: 'claude-3-haiku-20240307',
options: {
'temperature': 0.7,
'max_tokens': 1500,
},
),
systemPrompt: 'You are a helpful assistant. Be concise and friendly.',
);
} catch (e) {
_showError('Failed to initialize AI: $e');
}
}
void _showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
void _handleSubmitted(String text) async {
if (text.trim().isEmpty) return;
_textController.clear();
setState(() {
_messages.add(ChatMessage(
text: text,
isUser: true,
));
_isTyping = true;
});
if (_client == null) {
_showError('AI client not initialized');
setState(() {
_isTyping = false;
});
return;
}
try {
final response = await _client!.chat(text);
setState(() {
_messages.add(ChatMessage(
text: response.text,
isUser: false,
));
_isTyping = false;
});
} catch (e) {
_showError('Error getting AI response: $e');
setState(() {
_isTyping = false;
});
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AI Chat App'),
),
body: Column(
children: [
Flexible(
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
reverse: true,
itemCount: _messages.length,
itemBuilder: (_, index) => _messages[_messages.length - 1 - index],
),
),
if (_isTyping)
const Padding(
padding: EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
CircularProgressIndicator(),
SizedBox(width: 8),
Text('AI is typing...'),
],
),
),
const Divider(height: 1.0),
Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
),
child: _buildTextComposer(),
),
],
),
);
}
Widget _buildTextComposer() {
return IconTheme(
data: IconThemeData(color: Theme.of(context).colorScheme.primary),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
children: [
Flexible(
child: TextField(
controller: _textController,
onSubmitted: _handleSubmitted,
decoration: const InputDecoration.collapsed(
hintText: 'Send a message',
),
),
),
Container(
margin: const EdgeInsets.symmetric(horizontal: 4.0),
child: IconButton(
icon: const Icon(Icons.send),
onPressed: () => _handleSubmitted(_textController.text),
),
),
],
),
),
);
}
void dispose() {
_mcpLlm.shutdown();
_textController.dispose();
super.dispose();
}
}
class ChatMessage extends StatelessWidget {
final String text;
final bool isUser;
const ChatMessage({
super.key,
required this.text,
required this.isUser,
});
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(right: 16.0),
child: CircleAvatar(
child: Text(isUser ? 'You' : 'AI'),
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isUser ? 'You' : 'AI Assistant',
style: Theme.of(context).textTheme.titleMedium,
),
Container(
margin: const EdgeInsets.only(top: 5.0),
child: Text(text),
),
],
),
),
],
),
);
}
}
이제 애플리케이션을 실행하고 AI와 채팅해 보세요:
flutter run
이 간단한 채팅 앱은 다음과 같은 기능을 제공합니다:
이 포스트에서는 mcp_llm 패키지의 기본 개념과 사용법을 소개했습니다. 다음 단계에서는 더 고급 기능을 탐색할 수 있습니다:
mcp_llm은 Dart와 Flutter에서 대규모 언어 모델을 통합하고 활용할 수 있는 강력한 패키지입니다. MCP 생태계와의 통합을 통해 다양한 AI 모델과 외부 도구, 리소스를 연결하여 더 풍부한 AI 애플리케이션을 구축할 수 있습니다.
이 패키지는 mcp_client와 mcp_server를 보완하며, 세 패키지를 함께 사용하면 완전한 MCP 생태계를 구축할 수 있습니다. 다음 포스트에서는 LlmClient의 고급 기능과 mcp_client 연동에 대해 더 자세히 살펴보겠습니다.
앞으로의 시리즈를 기대해 주세요!
이 글이 도움이 되셨다면, 패트론을 통해 개발 활동을 지원해 주세요. 여러분의 후원은 더 많은 무료 콘텐츠를 만드는 데 큰 힘이 됩니다.

태그: #Flutter #AI #MCP #LLM #Dart #Claude #OpenAI #ModelContextProtocol #AIIntegration