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)))); // Отключить для самоподписанных сертификатов
Отключение проверки сертификатов (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
}
Обучение на примерах
- MRTR-клиент — цикл раундов от начала до конца
- HTTP-клиент (корневые каталоги)
- Клиент сэмплирования