Skip to content

Latest commit

 

History

History
578 lines (406 loc) · 39.7 KB

File metadata and controls

578 lines (406 loc) · 39.7 KB

HTTPS Streaming с протоколом Model Context Protocol (MCP)

Эта глава представляет подробное руководство по реализации безопасного, масштабируемого и потокового взаимодействия в реальном времени с использованием протокола Model Context Protocol (MCP) через HTTPS. В ней рассматриваются мотивация для использования потоков, доступные механизмы передачи данных, реализация потокового HTTP в MCP, лучшие практики безопасности, переход от SSE и практические рекомендации по созданию собственных приложений MCP с потоковой передачей.

Механизмы передачи данных и потоковая передача в MCP

Этот раздел исследует различные механизмы передачи данных, доступные в MCP, и их роль в обеспечении потоковой передачи для взаимодействия в реальном времени между клиентами и серверами.

Что такое механизм передачи данных?

Механизм передачи данных определяет, как информация обменивается между клиентом и сервером. MCP поддерживает несколько типов передачи данных, чтобы соответствовать различным условиям и требованиям:

  • stdio: Стандартный ввод/вывод, подходит для локальных и CLI-инструментов. Простой, но не подходит для веба или облака.
  • SSE (Server-Sent Events): Позволяет серверам отправлять обновления в реальном времени клиентам через HTTP. Хорошо подходит для веб-интерфейсов, но ограничен в масштабируемости и гибкости.
  • Streamable HTTP: Современный HTTP-транспорт для потоковой передачи, поддерживающий уведомления и лучшую масштабируемость. Рекомендуется для большинства производственных и облачных сценариев.

Сравнительная таблица

Посмотрите таблицу ниже, чтобы понять различия между этими механизмами передачи данных:

Механизм передачи Обновления в реальном времени Потоковая передача Масштабируемость Сценарий использования
stdio Нет Нет Низкая Локальные CLI-инструменты
SSE Да Да Средняя Веб, обновления в реальном времени
Streamable HTTP Да Да Высокая Облако, многоклиентские приложения

Совет: Выбор правильного механизма передачи данных влияет на производительность, масштабируемость и пользовательский опыт. Streamable HTTP рекомендуется для современных, масштабируемых и облачных приложений.

Обратите внимание на механизмы передачи stdio и SSE, которые были рассмотрены в предыдущих главах, и на то, что Streamable HTTP является транспортом, охватываемым в этой главе.

Потоковая передача: концепции и мотивация

Понимание основных концепций и мотивации потоковой передачи важно для реализации эффективных систем взаимодействия в реальном времени.

Потоковая передача — это техника сетевого программирования, которая позволяет отправлять и получать данные небольшими, управляемыми частями или в виде последовательности событий, вместо ожидания готовности полного ответа. Это особенно полезно для:

  • Больших файлов или наборов данных.
  • Обновлений в реальном времени (например, чат, индикаторы прогресса).
  • Длительных вычислений, где важно информировать пользователя о ходе выполнения.

Вот что нужно знать о потоковой передаче на высоком уровне:

  • Данные доставляются постепенно, а не все сразу.
  • Клиент может обрабатывать данные по мере их поступления.
  • Снижает воспринимаемую задержку и улучшает пользовательский опыт.

Зачем использовать потоковую передачу?

Причины использования потоковой передачи следующие:

  • Пользователи получают обратную связь немедленно, а не только в конце.
  • Обеспечивает приложения в реальном времени и отзывчивые интерфейсы.
  • Более эффективное использование сетевых и вычислительных ресурсов.

Простой пример: HTTP-потоковый сервер и клиент

Вот простой пример того, как можно реализовать потоковую передачу:

Python

Сервер (Python, с использованием FastAPI и StreamingResponse):

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import time

app = FastAPI()

async def event_stream():
    for i in range(1, 6):
        yield f"data: Message {i}\n\n"
        time.sleep(1)

@app.get("/stream")
def stream():
    return StreamingResponse(event_stream(), media_type="text/event-stream")

Клиент (Python, с использованием requests):

import requests

with requests.get("http://localhost:8000/stream", stream=True) as r:
    for line in r.iter_lines():
        if line:
            print(line.decode())

Этот пример демонстрирует сервер, отправляющий серию сообщений клиенту по мере их готовности, вместо ожидания готовности всех сообщений.

Как это работает:

  • Сервер передает каждое сообщение по мере его готовности.
  • Клиент получает и выводит каждую часть данных по мере их поступления.

Требования:

  • Сервер должен использовать потоковый ответ (например, StreamingResponse в FastAPI).
  • Клиент должен обрабатывать ответ как поток (stream=True в requests).
  • Content-Type обычно text/event-stream или application/octet-stream.

Java

Сервер (Java, с использованием Spring Boot и Server-Sent Events):

@RestController
public class CalculatorController {

    @GetMapping(value = "/calculate", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> calculate(@RequestParam double a,
                                                   @RequestParam double b,
                                                   @RequestParam String op) {
        
        double result;
        switch (op) {
            case "add": result = a + b; break;
            case "sub": result = a - b; break;
            case "mul": result = a * b; break;
            case "div": result = b != 0 ? a / b : Double.NaN; break;
            default: result = Double.NaN;
        }

        return Flux.<ServerSentEvent<String>>just(
                    ServerSentEvent.<String>builder()
                        .event("info")
                        .data("Calculating: " + a + " " + op + " " + b)
                        .build(),
                    ServerSentEvent.<String>builder()
                        .event("result")
                        .data(String.valueOf(result))
                        .build()
                )
                .delayElements(Duration.ofSeconds(1));
    }
}

Клиент (Java, с использованием Spring WebFlux WebClient):

@SpringBootApplication
public class CalculatorClientApplication implements CommandLineRunner {

    private final WebClient client = WebClient.builder()
            .baseUrl("http://localhost:8080")
            .build();

    @Override
    public void run(String... args) {
        client.get()
                .uri(uriBuilder -> uriBuilder
                        .path("/calculate")
                        .queryParam("a", 7)
                        .queryParam("b", 5)
                        .queryParam("op", "mul")
                        .build())
                .accept(MediaType.TEXT_EVENT_STREAM)
                .retrieve()
                .bodyToFlux(String.class)
                .doOnNext(System.out::println)
                .blockLast();
    }
}

Примечания к реализации на Java:

  • Используется реактивный стек Spring Boot с Flux для потоковой передачи.
  • ServerSentEvent обеспечивает структурированную потоковую передачу событий с типами событий.
  • WebClient с bodyToFlux() позволяет реактивное потребление потоков.
  • delayElements() симулирует время обработки между событиями.
  • События могут иметь типы (info, result) для лучшей обработки клиентом.

Сравнение: классическая потоковая передача vs MCP-потоковая передача

Различия между классической потоковой передачей и потоковой передачей в MCP можно представить следующим образом:

Особенность Классическая HTTP-потоковая передача MCP-потоковая передача (уведомления)
Основной ответ Передается частями Единый, в конце
Обновления прогресса Отправляются как части данных Отправляются как уведомления
Требования к клиенту Должен обрабатывать поток Должен реализовать обработчик сообщений
Сценарий использования Большие файлы, потоки токенов ИИ Прогресс, логи, обратная связь в реальном времени

Основные наблюдаемые различия

Кроме того, вот некоторые ключевые различия:

  • Паттерн взаимодействия:

    • Классическая HTTP-потоковая передача: Использует простую кодировку chunked transfer для отправки данных частями.
    • MCP-потоковая передача: Использует структурированную систему уведомлений с протоколом JSON-RPC.
  • Формат сообщений:

    • Классическая HTTP: Простые текстовые части с переводами строк.
    • MCP: Структурированные объекты LoggingMessageNotification с метаданными.
  • Реализация клиента:

    • Классическая HTTP: Простой клиент, обрабатывающий потоковые ответы.
    • MCP: Более сложный клиент с обработчиком сообщений для обработки различных типов сообщений.
  • Обновления прогресса:

    • Классическая HTTP: Прогресс является частью основного потока ответа.
    • MCP: Прогресс отправляется через отдельные уведомления, а основной ответ приходит в конце.

Рекомендации

Есть несколько рекомендаций по выбору между реализацией классической потоковой передачи (как показано выше через /stream) и выбором потоковой передачи через MCP.

  • Для простых потребностей потоковой передачи: Классическая HTTP-потоковая передача проще в реализации и достаточна для базовых потребностей.
  • Для сложных, интерактивных приложений: MCP-потоковая передача предоставляет более структурированный подход с богатыми метаданными и разделением между уведомлениями и конечными результатами.
  • Для приложений с ИИ: Система уведомлений MCP особенно полезна для длительных задач ИИ, где важно информировать пользователей о ходе выполнения.

Потоковая передача в MCP

Итак, вы уже видели рекомендации и сравнения между классической потоковой передачей и потоковой передачей в MCP. Давайте подробно рассмотрим, как можно использовать потоковую передачу в MCP.

Понимание того, как работает потоковая передача в рамках MCP, важно для создания отзывчивых приложений, которые предоставляют обратную связь в реальном времени пользователям во время длительных операций.

В MCP потоковая передача заключается не в отправке основного ответа частями, а в отправке уведомлений клиенту во время обработки запроса инструментом. Эти уведомления могут включать обновления прогресса, логи или другие события.

Как это работает

Основной результат все равно отправляется как единый ответ. Однако уведомления могут быть отправлены как отдельные сообщения во время обработки, тем самым обновляя клиента в реальном времени. Клиент должен уметь обрабатывать и отображать эти уведомления.

Что такое уведомление?

Мы упомянули "уведомление", что это значит в контексте MCP?

Уведомление — это сообщение, отправляемое сервером клиенту для информирования о ходе выполнения, статусе или других событиях во время длительной операции. Уведомления улучшают прозрачность и пользовательский опыт.

Например, клиент должен отправить уведомление после завершения начального рукопожатия с сервером.

Уведомление выглядит как JSON-сообщение:

{
  jsonrpc: "2.0";
  method: string;
  params?: {
    [key: string]: unknown;
  };
}

Уведомления относятся к теме в MCP, называемой "Logging".

Чтобы включить логирование, сервер должен активировать эту функцию/возможность следующим образом:

{
  "capabilities": {
    "logging": {}
  }
}

Note

В зависимости от используемого SDK логирование может быть включено по умолчанию, либо его нужно явно включить в конфигурации сервера.

Существуют различные типы уведомлений:

Уровень Описание Пример использования
debug Подробная информация для отладки Точки входа/выхода функции
info Общие информационные сообщения Обновления хода выполнения
notice Нормальные, но значительные события Изменения конфигурации
warning Предупреждающие условия Использование устаревших функций
error Ошибочные условия Сбои операций
critical Критические условия Сбои компонентов системы
alert Требуется немедленное действие Обнаружение повреждения данных
emergency Система неработоспособна Полный сбой системы

Реализация уведомлений в MCP

Чтобы реализовать уведомления в MCP, необходимо настроить как серверную, так и клиентскую стороны для обработки обновлений в реальном времени. Это позволяет вашему приложению предоставлять немедленную обратную связь пользователям во время длительных операций.

Серверная сторона: отправка уведомлений

Начнем с серверной стороны. В MCP вы определяете инструменты, которые могут отправлять уведомления во время обработки запросов. Сервер использует объект контекста (обычно ctx) для отправки сообщений клиенту.

Python

@mcp.tool(description="A tool that sends progress notifications")
async def process_files(message: str, ctx: Context) -> TextContent:
    await ctx.info("Processing file 1/3...")
    await ctx.info("Processing file 2/3...")
    await ctx.info("Processing file 3/3...")
    return TextContent(type="text", text=f"Done: {message}")

В приведенном примере инструмент process_files отправляет три уведомления клиенту по мере обработки каждого файла. Метод ctx.info() используется для отправки информационных сообщений.

Кроме того, чтобы включить уведомления, убедитесь, что ваш сервер использует потоковый транспорт (например, streamable-http), а ваш клиент реализует обработчик сообщений для обработки уведомлений. Вот как можно настроить сервер для использования транспорта streamable-http:

mcp.run(transport="streamable-http")

.NET

[Tool("A tool that sends progress notifications")]
public async Task<TextContent> ProcessFiles(string message, ToolContext ctx)
{
    await ctx.Info("Processing file 1/3...");
    await ctx.Info("Processing file 2/3...");
    await ctx.Info("Processing file 3/3...");
    return new TextContent
    {
        Type = "text",
        Text = $"Done: {message}"
    };
}

В этом примере на .NET инструмент ProcessFiles помечен атрибутом Tool и отправляет три уведомления клиенту по мере обработки каждого файла. Метод ctx.Info() используется для отправки информационных сообщений.

Чтобы включить уведомления в вашем MCP-сервере на .NET, убедитесь, что вы используете потоковый транспорт:

var builder = McpBuilder.Create();
await builder
    .UseStreamableHttp() // Enable streamable HTTP transport
    .Build()
    .RunAsync();

Клиентская сторона: получение уведомлений

Клиент должен реализовать обработчик сообщений для обработки и отображения уведомлений по мере их поступления.

Python

async def message_handler(message):
    if isinstance(message, types.ServerNotification):
        print("NOTIFICATION:", message)
    else:
        print("SERVER MESSAGE:", message)

async with ClientSession(
   read_stream, 
   write_stream,
   logging_callback=logging_collector,
   message_handler=message_handler,
) as session:

В приведенном коде функция message_handler проверяет, является ли входящее сообщение уведомлением. Если да, оно выводит уведомление; в противном случае оно обрабатывается как обычное сообщение сервера. Также обратите внимание, как ClientSession инициализируется с message_handler для обработки входящих уведомлений.

.NET

// Define a message handler
void MessageHandler(IJsonRpcMessage message)
{
    if (message is ServerNotification notification)
    {
        Console.WriteLine($"NOTIFICATION: {notification}");
    }
    else
    {
        Console.WriteLine($"SERVER MESSAGE: {message}");
    }
}

// Create and use a client session with the message handler
var clientOptions = new ClientSessionOptions
{
    MessageHandler = MessageHandler,
    LoggingCallback = (level, message) => Console.WriteLine($"[{level}] {message}")
};

using var client = new ClientSession(readStream, writeStream, clientOptions);
await client.InitializeAsync();

// Now the client will process notifications through the MessageHandler

В этом примере на .NET функция MessageHandler проверяет, является ли входящее сообщение уведомлением. Если да, оно выводит уведомление; в противном случае оно обрабатывается как обычное сообщение сервера. ClientSession инициализируется с обработчиком сообщений через ClientSessionOptions.

Чтобы включить уведомления, убедитесь, что ваш сервер использует потоковый транспорт (например, streamable-http), а ваш клиент реализует обработчик сообщений для обработки уведомлений.

Уведомления о прогрессе и сценарии

Этот раздел объясняет концепцию уведомлений о прогрессе в MCP, их важность и как их реализовать с использованием Streamable HTTP. Также вы найдете практическое задание для закрепления материала.

Уведомления о прогрессе — это сообщения в реальном времени, отправляемые сервером клиенту во время длительных операций. Вместо ожидания завершения всего процесса сервер информирует клиента о текущем статусе. Это улучшает прозрачность, пользовательский опыт и упрощает отладку.

Пример:


"Processing document 1/10"
"Processing document 2/10"
...
"Processing complete!"

Зачем использовать уведомления о прогрессе?

Уведомления о прогрессе важны по нескольким причинам:

  • Лучший пользовательский опыт: Пользователи видят обновления по мере выполнения работы, а не только в конце.
  • Обратная связь в реальном времени: Клиенты могут отображать индикаторы прогресса или логи, делая приложение более отзывчивым.
  • Упрощенная отладка и мониторинг: Разработчики и пользователи могут видеть, где процесс может быть медленным или зависшим.

Как реализовать уведомления о прогрессе

Вот как можно реализовать уведомления о прогрессе в MCP:

  • На сервере: Используйте ctx.info() или ctx.log() для отправки уведомлений по мере обработки каждого элемента. Это отправляет сообщение клиенту до готовности основного результата.
  • На клиенте: Реализуйте обработчик сообщений, который слушает и отображает уведомления по мере их поступления. Этот обработчик различает уведомления и конечный результат.

Пример сервера:

Python

@mcp.tool(description="A tool that sends progress notifications")
async def process_files(message: str, ctx: Context) -> TextContent:
    for i in range(1, 11):
        await ctx.info(f"Processing document {i}/10")
    await ctx.info("Processing complete!")
    return TextContent(type="text", text=f"Done: {message}")

Пример клиента:

Python

async def message_handler(message):
    if isinstance(message, types.ServerNotification):
        print("NOTIFICATION:", message)
    else:
        print("SERVER MESSAGE:", message)

Соображения по безопасности

При реализации MCP-серверов с HTTP-транспортами безопасность становится первоочередной задачей, требующей внимательного подхода к множеству векторов атак и механизмов защиты.

Обзор

Безопасность критически важна при открытии MCP-серверов через HTTP. Streamable HTTP вводит новые поверхности атаки и требует тщательной настройки.

Основные моменты

  • Проверка заголовка Origin: Всегда проверяйте заголовок Origin, чтобы предотвратить атаки DNS rebinding.
  • Привязка к localhost: Для локальной разработки привязывайте серверы к localhost, чтобы избежать их публичного доступа.
  • Аутентификация: Реализуйте аутентификацию (например, API-ключи, OAuth) для производственных развертываний.
  • CORS: Настройте политики Cross-Origin Resource Sharing (CORS) для ограничения доступа.
  • HTTPS: Используйте HTTPS в производственной среде для шифрования трафика.

Лучшие практики

  • Никогда не доверяйте входящим запросам без проверки.
  • Логируйте и мониторьте все обращения и ошибки.
  • Регулярно обновляйте зависимости для устранения уязвимостей.

Проблемы

  • Баланс между безопасностью и удобством разработки.
  • Обеспечение совместимости с различными клиентскими средами.

Переход от SSE к Streamable HTTP

Для приложений, которые в настоящее время используют Server-Sent Events (SSE), переход на Streamable HTTP предоставляет расширенные возможности и лучшую долгосрочную устойчивость для ваших реализаций MCP.

Зачем переходить?

Есть две веские причины перейти с SSE на Streamable HTTP:

  • Streamable HTTP обеспечивает лучшую масштабируемость, совместимость и более богатую поддержку уведомлений по сравнению с SSE.
  • Это рекомендуемый транспорт для новых приложений MCP.

Шаги миграции

Вот как можно перейти с SSE на Streamable HTTP в ваших приложениях MCP:

  • Обновите серверный код, указав transport="streamable-http" в mcp.run().
  • Обновите клиентский код, заменив SSE-клиент на streamablehttp_client.
  • Реализуйте обработчик сообщений в клиенте для обработки уведомлений.
  • Проверьте совместимость с существующими инструментами и рабочими процессами.

Поддержание совместимости

Рекомендуется сохранять совместимость с существующими SSE-клиентами в процессе миграции. Вот несколько стратегий:

  • Вы можете поддерживать оба транспорта, SSE и Streamable HTTP, запустив их на разных конечных точках.
  • Постепенно переводите клиентов на новый транспорт.

Проблемы

Убедитесь, что вы решаете следующие проблемы в процессе миграции:

  • Обновление всех клиентов
  • Обработка различий в доставке уведомлений

Вопросы безопасности

Безопасность должна быть главным приоритетом при реализации любого сервера, особенно при использовании HTTP-транспортов, таких как Streamable HTTP в MCP.

При реализации MCP-серверов с HTTP-транспортами безопасность становится критически важной задачей, требующей тщательного внимания к множеству векторов атак и механизмов защиты.

Обзор

Безопасность имеет ключевое значение при открытии MCP-серверов через HTTP. Streamable HTTP вводит новые уязвимости и требует тщательной настройки.

Вот основные аспекты безопасности:

  • Проверка заголовка Origin: Всегда проверяйте заголовок Origin, чтобы предотвратить атаки типа DNS rebinding.
  • Привязка к localhost: Для локальной разработки привязывайте серверы к localhost, чтобы избежать их публикации в интернете.
  • Аутентификация: Реализуйте аутентификацию (например, API-ключи, OAuth) для рабочих сред.
  • CORS: Настройте политику Cross-Origin Resource Sharing (CORS) для ограничения доступа.
  • HTTPS: Используйте HTTPS в рабочей среде для шифрования трафика.

Лучшие практики

Кроме того, вот несколько лучших практик для обеспечения безопасности вашего MCP-сервера потоковой передачи:

  • Никогда не доверяйте входящим запросам без проверки.
  • Логируйте и отслеживайте все обращения и ошибки.
  • Регулярно обновляйте зависимости для устранения уязвимостей.

Проблемы

Вы столкнетесь с некоторыми трудностями при реализации безопасности в MCP-серверах потоковой передачи:

  • Баланс между безопасностью и удобством разработки
  • Обеспечение совместимости с различными клиентскими средами

Задание: Создайте собственное потоковое MCP-приложение

Сценарий: Создайте MCP-сервер и клиент, где сервер обрабатывает список элементов (например, файлов или документов) и отправляет уведомление для каждого обработанного элемента. Клиент должен отображать каждое уведомление по мере его получения.

Шаги:

  1. Реализуйте серверный инструмент, который обрабатывает список и отправляет уведомления для каждого элемента.
  2. Реализуйте клиент с обработчиком сообщений для отображения уведомлений в реальном времени.
  3. Протестируйте вашу реализацию, запустив сервер и клиент, и наблюдайте за уведомлениями.

Решение

Дополнительные материалы и что дальше?

Чтобы продолжить изучение MCP-потоков и расширить свои знания, в этом разделе представлены дополнительные ресурсы и предложены следующие шаги для создания более сложных приложений.

Дополнительные материалы

Что дальше?

  • Попробуйте создать более сложные MCP-инструменты, использующие потоковую передачу для аналитики в реальном времени, чатов или совместного редактирования.
  • Изучите интеграцию MCP-потоков с фронтенд-фреймворками (React, Vue и т.д.) для обновления интерфейса в реальном времени.
  • Далее: Использование AI Toolkit для VSCode

Отказ от ответственности:
Этот документ был переведен с использованием сервиса автоматического перевода Co-op Translator. Хотя мы стремимся к точности, пожалуйста, учитывайте, что автоматические переводы могут содержать ошибки или неточности. Оригинальный документ на его родном языке следует считать авторитетным источником. Для получения критически важной информации рекомендуется профессиональный перевод человеком. Мы не несем ответственности за любые недоразумения или неправильные интерпретации, возникающие в результате использования данного перевода.