Перейти к основному содержимому

HTTP-транспорт

Помимо stdio, клиенты Neva поддерживают подключение к MCP-серверам по потоковому HTTP.

Что делает connect()

connect() открывает соединение единственным запросом server/discover — рукопожатия initialize / initialized больше нет. Client::discover() — явный вызов; Client::init() остаётся псевдонимом для обратной совместимости.

Каждый последующий POST несёт:

  • заголовок MCP-Protocol-Version, зафиксированный на 2026-07-28;
  • _meta с io.modelcontextprotocol/protocolVersion и io.modelcontextprotocol/clientCapabilities;
  • заголовки маршрутизации Mcp-Method, Mcp-Name и все Mcp-Param-{name}, которых требует схема вызываемого инструмента.

Всё это neva формирует за вас. Сервер отклоняет запрос, у которого заголовки расходятся с телом, — поэтому, если вы находитесь за прокси, переписывающим заголовки, при внезапных 400 начинайте искать именно там.

Client::server_info читается из _meta["io.modelcontextprotocol/serverInfo"], который несёт каждый результат, — в результате discovery этого поля больше нет.

Отдельного SSE-потока GET в транспорте больше нет — neva его не открывает под капотом connect(). Серверные уведомления вместо этого приходят на запросе subscriptions/listen: ответ на этот POST и есть поток, а открывает его Client::listen.

Работа с легаси-сервером

Клиент работает в двойном режиме. Если server/discover отклонён на уровне протокола — MethodNotFound, InvalidRequest либо ответ не в формате JSON-RPC или с неизвестным кодом, — он откатывается к легаси-рукопожатию initialize и до конца соединения говорит с этим узлом на старом протоколе: Mcp-Session-Id, отдельный SSE-поток GET, серверный push для сэмплирования, корневых каталогов и логов, без MRTR и без заголовков маршрутизации.

Сетевые ошибки отката не вызывают. Переключение происходит один раз на соединение, необратимо и до любого другого трафика — то есть сборка с legacy-spec не нужна только ради подключения к старому серверу.

with_mcp_version(...) на клиенте по-прежнему существует, но выбирает лишь версию, о которой договаривается откат; он никогда не заставит server/discover отклонить корректный сервер MCP 2026-07-28.

Версия, которую клиент предлагает в этом рукопожатии, — предложение, а не требование: сервер, который её не поддерживает, отвечает той, которую поддерживает, и соединение продолжается, если клиент говорит и на ней. Соединение обрывает только версия, о которой neva ничего не знает: настаивать на предложенной версии значило бы отказывать каждому серверу, который на шаг старше клиента, — а ровно ради этого случая согласование и существует.

Подключение через HTTP

Используйте with_http() для настройки клиента на HTTP-транспорт:

use neva::prelude::*;

#[tokio::main]
async fn main() -> Result<(), Error> {
let mut client = Client::new()
.with_options(|opt| opt
.with_http(|http| http
.bind("127.0.0.1:3000")));

client.connect().await?;

// Вызов инструментов, чтение ресурсов и т.д.

client.disconnect().await
}

Конфигурация HTTP по умолчанию

Для быстрого старта используйте with_default_http(), который подключается к 127.0.0.1:3000 с конечной точкой /mcp по умолчанию:

let mut client = Client::new()
.with_options(|opt| opt.with_default_http());

TLS / HTTPS

Для подключения к HTTPS-серверу настройте TLS на клиенте:

let mut client = Client::new()
.with_options(|opt| opt
.with_http(|http| http
.bind("localhost:7878")
.with_tls(|tls| tls
.with_certs_verification(false)))); // Отключить для самоподписанных сертификатов
warning

Отключение проверки сертификатов (with_certs_verification(false)) предназначено только для локальной разработки. В продакшене всегда используйте корректно подписанный сертификат и не отключайте проверку.

Аутентификация по токену Bearer

Если MCP-сервер требует JWT-аутентификацию, прикрепите токен Bearer с помощью with_auth():

const ACCESS_TOKEN: &str = "eyJhbGci..."; // Ваш JWT Bearer токен

let mut client = Client::new()
.with_options(|opt| opt
.with_http(|http| http
.bind("localhost:7878")
.with_auth(ACCESS_TOKEN)));

client.connect().await?;

Токен отправляется в заголовке Authorization: Bearer <token> при каждом запросе.

Полный пример: HTTPS + Auth

use neva::prelude::*;

const ACCESS_TOKEN: &str = "eyJhbGci...";

#[tokio::main]
async fn main() -> Result<(), Error> {
let mut client = Client::new()
.with_options(|opt| opt
.with_http(|http| http
.bind("localhost:7878")
.with_tls(|tls| tls
.with_certs_verification(false))
.with_auth(ACCESS_TOKEN)));

client.connect().await?;

let result = client.call_tool("my_tool", ("input", "value")).await?;
println!("{:?}", result.content);

client.disconnect().await
}

Обучение на примерах