Соедините модели ИИ с живыми данными с помощью Model Context Protocol (MCP)
Почему MCP важен
Модели ИИ, обученные на статических данных, мощны, но ограничены, если не имеют доступа к актуальной информации, внешним инструментам или контексту в реальном времени. Model Context Protocol (MCP) предлагает структурированный способ подключить модели к динамическим ресурсам и модульным инструментам: системы могут получать данные, выполнять вычисления и поддерживать контекст диалога в реальном времени.
Основные компоненты
MCP опирается на три ключевых концепта: ресурсы, инструменты и сообщения. Ресурсы представляют внешние данные (документы, API, наборы данных), инструменты — это подключаемые обработчики действий (анализ, суммаризация, поиск), а сообщения формируют контекстную память взаимодействий между клиентами и серверами.
В руководстве сначала определяются базовые структуры данных на Python:
import json
import asyncio
from dataclasses import dataclass, asdict
from typing import Dict, List, Any, Optional, Callable
from datetime import datetime
import random
@dataclass
class Resource:
uri: str
name: str
description: str
mime_type: str
content: Any = None
@dataclass
class Tool:
name: str
description: str
parameters: Dict[str, Any]
handler: Optional[Callable] = None
@dataclass
class Message:
role: str
content: str
timestamp: str = None
def __post_init__(self):
if not self.timestamp:
self.timestamp = datetime.now().isoformat()
Сервер: управление ресурсами и инструментами
MCPServer инкапсулирует доступные ресурсы и инструменты и предоставляет асинхронные API для получения и выполнения. Его обязанности включают регистрацию ресурсов и инструментов, отдачу содержимого и вызов обработчиков инструментов. Асинхронная архитектура делает I/O эффективным.
class MCPServer:
def __init__(self, name: str):
self.name = name
self.resources: Dict[str, Resource] = {}
self.tools: Dict[str, Tool] = {}
self.capabilities = {"resources": True, "tools": True, "prompts": True, "logging": True}
print(f"✓ MCP Server '{name}' initialized with capabilities: {list(self.capabilities.keys())}")
def register_resource(self, resource: Resource) -> None:
self.resources[resource.uri] = resource
print(f" → Resource registered: {resource.name} ({resource.uri})")
def register_tool(self, tool: Tool) -> None:
self.tools[tool.name] = tool
print(f" → Tool registered: {tool.name}")
async def get_resource(self, uri: str) -> Optional[Resource]:
await asyncio.sleep(0.1)
return self.resources.get(uri)
async def execute_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any:
if tool_name not in self.tools:
raise ValueError(f"Tool '{tool_name}' not found")
tool = self.tools[tool_name]
if tool.handler:
return await tool.handler(**arguments)
return {"status": "executed", "tool": tool_name, "args": arguments}
def list_resources(self) -> List[Dict[str, str]]:
return [{"uri": r.uri, "name": r.name, "description": r.description} for r in self.resources.values()]
def list_tools(self) -> List[Dict[str, Any]]:
return [{"name": t.name, "description": t.description, "parameters": t.parameters} for t in self.tools.values()]
Клиент: контекст и взаимодействие
MCPClient подключается к одному или нескольким серверам MCP, запрашивает ресурсы, получает содержимое и вызывает инструменты. Он также поддерживает контекст — список объектов Message — который фиксирует системные события и может использоваться моделями для принятия решений.
class MCPClient:
def __init__(self, client_id: str):
self.client_id = client_id
self.connected_servers: Dict[str, MCPServer] = {}
self.context: List[Message] = []
print(f"n✓ MCP Client '{client_id}' initialized")
def connect_server(self, server: MCPServer) -> None:
self.connected_servers[server.name] = server
print(f" → Connected to server: {server.name}")
async def query_resources(self, server_name: str) -> List[Dict[str, str]]:
if server_name not in self.connected_servers:
raise ValueError(f"Not connected to server: {server_name}")
return self.connected_servers[server_name].list_resources()
async def fetch_resource(self, server_name: str, uri: str) -> Optional[Resource]:
if server_name not in self.connected_servers:
raise ValueError(f"Not connected to server: {server_name}")
server = self.connected_servers[server_name]
resource = await server.get_resource(uri)
if resource:
self.add_to_context(Message(role="system", content=f"Fetched resource: {resource.name}"))
return resource
async def call_tool(self, server_name: str, tool_name: str, **kwargs) -> Any:
if server_name not in self.connected_servers:
raise ValueError(f"Not connected to server: {server_name}")
server = self.connected_servers[server_name]
result = await server.execute_tool(tool_name, kwargs)
self.add_to_context(Message(role="system", content=f"Tool '{tool_name}' executed"))
return result
def add_to_context(self, message: Message) -> None:
self.context.append(message)
def get_context(self) -> List[Dict[str, Any]]:
return [asdict(msg) for msg in self.context]
Инструменты: модульные обработчики
Инструменты реализованы как асинхронные обработчики, которые принимают вход и возвращают структурированные результаты. В примере использованы заглушки для анализа сентимента, суммаризации текста и поиска по знаниям, чтобы показать, как инструменты подключаются к MCP.
async def analyze_sentiment(text: str) -> Dict[str, Any]:
await asyncio.sleep(0.2)
sentiments = ["positive", "negative", "neutral"]
return {"text": text, "sentiment": random.choice(sentiments), "confidence": round(random.uniform(0.7, 0.99), 2)}
async def summarize_text(text: str, max_length: int = 100) -> Dict[str, str]:
await asyncio.sleep(0.15)
summary = text[:max_length] + "..." if len(text) > max_length else text
return {"original_length": len(text), "summary": summary, "compression_ratio": round(len(summary) / len(text), 2)}
async def search_knowledge(query: str, top_k: int = 3) -> List[Dict[str, Any]]:
await asyncio.sleep(0.25)
mock_results = [{"title": f"Result {i+1} for '{query}'", "score": round(random.uniform(0.5, 1.0), 2)} for i in range(top_k)]
return sorted(mock_results, key=lambda x: x["score"], reverse=True)
Полная демонстрация
Функция run_mcp_demo поднимает сервер, регистрирует ресурсы и инструменты, подключает клиента и выполняет серию демонстрационных сценариев: перечисление ресурсов, получение данных, анализ сентимента, суммаризация текста, поиск по базе знаний и проверка контекстного окна клиента.
async def run_mcp_demo():
print("=" * 60)
print("MODEL CONTEXT PROTOCOL (MCP) - ADVANCED TUTORIAL")
print("=" * 60)
print("n[1] Setting up MCP Server...")
server = MCPServer("knowledge-server")
print("n[2] Registering resources...")
server.register_resource(Resource(uri="docs://python-guide", name="Python Programming Guide", description="Comprehensive Python documentation", mime_type="text/markdown", content="# Python GuidenPython is a high-level programming language..."))
server.register_resource(Resource(uri="data://sales-2024", name="2024 Sales Data", description="Annual sales metrics", mime_type="application/json", content={"q1": 125000, "q2": 142000, "q3": 138000, "q4": 165000}))
print("n[3] Registering tools...")
server.register_tool(Tool(name="analyze_sentiment", description="Analyze sentiment of text", parameters={"text": {"type": "string", "required": True}}, handler=analyze_sentiment))
server.register_tool(Tool(name="summarize_text", description="Summarize long text", parameters={"text": {"type": "string", "required": True}, "max_length": {"type": "integer", "default": 100}}, handler=summarize_text))
server.register_tool(Tool(name="search_knowledge", description="Search knowledge base", parameters={"query": {"type": "string", "required": True}, "top_k": {"type": "integer", "default": 3}}, handler=search_knowledge))
client = MCPClient("demo-client")
client.connect_server(server)
print("n" + "=" * 60)
print("DEMONSTRATION: MCP IN ACTION")
print("=" * 60)
print("n[Demo 1] Listing available resources...")
resources = await client.query_resources("knowledge-server")
for res in resources:
print(f" • {res['name']}: {res['description']}")
print("n[Demo 2] Fetching sales data resource...")
sales_resource = await client.fetch_resource("knowledge-server", "data://sales-2024")
if sales_resource:
print(f" Data: {json.dumps(sales_resource.content, indent=2)}")
print("n[Demo 3] Analyzing sentiment...")
sentiment_result = await client.call_tool("knowledge-server", "analyze_sentiment", text="MCP is an amazing protocol for AI integration!")
print(f" Result: {json.dumps(sentiment_result, indent=2)}")
print("n[Demo 4] Summarizing text...")
summary_result = await client.call_tool("knowledge-server", "summarize_text", text="The Model Context Protocol enables seamless integration between AI models and external data sources...", max_length=50)
print(f" Summary: {summary_result['summary']}")
print("n[Demo 5] Searching knowledge base...")
search_result = await client.call_tool("knowledge-server", "search_knowledge", query="machine learning", top_k=3)
print(" Top results:")
for result in search_result:
print(f" - {result['title']} (score: {result['score']})")
print("n[Demo 6] Current context window...")
context = client.get_context()
print(f" Context length: {len(context)} messages")
for i, msg in enumerate(context[-3:], 1):
print(f" {i}. [{msg['role']}] {msg['content']}")
print("n" + "=" * 60)
print("✓ MCP Tutorial Complete!")
print("=" * 60)
print("nKey Takeaways:")
print("• MCP enables modular AI-to-resource connections")
print("• Resources provide context from external sources")
print("• Tools enable dynamic operations and actions")
print("• Async design supports efficient I/O operations")
if __name__ == "__main__":
import sys
if 'ipykernel' in sys.modules or 'google.colab' in sys.modules:
await run_mcp_demo()
else:
asyncio.run(run_mcp_demo())
Как это меняет дизайн ИИ
Отделяя данные и действия от весов модели, MCP позволяет рассматривать модели как агентов, которые могут запрашивать свежую информацию, вызывать специализированные инструменты и обновлять внутренний контекст. Это делает системы ИИ более адаптивными, проверяемыми и расширяемыми за счёт доменно-специфичных возможностей.
Практические шаги
Используйте шаблоны сервера и клиента, чтобы прототипировать коннекторы к реальным API, базам данных или вычислительным сервисам. Замените заглушки инструментов на рабочие реализации (NLP-пайплайны, индексы поиска, аналитические движки) и расширьте реестр ресурсов для продакшен-данных.