Smithery отказал fast-mcp. Claude за полчаса переехал на официальный SDK MCP — переписывание вышло дешёвым потому, что первый слой был тонким.
В smarts MCP подключили шесть дней назад. Я попросил Claude Code поднять три tool через gem fast-mcp:
get_contract_info(chain, address) — имя контракта, тег адаптера протокола, счётчики function/eventread_contract_state(chain, address, function_name, args?) — одиночный view/pure-вызов, кэш 60sget_uniswap_v3_pool(chain, address) — полная панель V3: цена в обе стороны, liquidity, tick, USD-TVLТот commit — 27ca82e feat(mcp): serve live contract data over MCP via fast-mcp, 2026-04-21 в 1:37. Около 30 минут. Ровно.
Шесть дней спустя, сегодня вечером — 2026-04-27 в 20:24 — я запушил другой commit: 4df08fa feat(mcp): switch to official mcp gem with Streamable HTTP transport.
Что произошло между ними?
Я отправил smarts на регистрацию в Smithery (директорию MCP-серверов). Их сканер сразу же ответил 405:
POST /mcp/sse → 405 Method Not Allowed
Понадобилось время, чтобы разобраться: fast-mcp 1.6.0 (последний release) до сих пор реализует HTTP+SSE-transport спецификации MCP 2024-11-05. А современные MCP-клиенты уже перешли на Streamable HTTP из спецификации 2025-03-26 — единый endpoint, POST + DELETE на одном URL, никакого отдельного /sse.
Иначе говоря: спецификация шагнула вперёд, а доминирующий community gem не догнал.
Ждать fast-mcp я не собирался. Слой MCP-сервера существует для того, чтобы его потребляли клиенты — соответствие спецификации весит больше, чем то, какой gem удобнее писать.
Заменил fast-mcp на официальный gem mcp 0.14.0 от Anthropic. На этот раунд я открыл новый thread в Amp (под капотом всё тот же Claude Opus, что и в Claude Code).
Что приземлилось при миграции:
Transport переложен
# Старый: fast-mcp монтируется автоматически на /mcp с /sse
# Новый: явный mount, единый endpoint
mount StreamableHTTPTransport.new(server, stateless: true), at: "/mcp"
Ключ — stateless: true. Сервер не держит per-session состояние в памяти, поэтому Puma может работать с workers > 0 и масштабироваться горизонтально без sticky session.
Реструктуризация Tool API
Старый стиль fast-mcp:
class GetContractInfoTool < ApplicationTool
arguments do
required(:chain).filled(:string)
required(:address).filled(:string)
end
def call(chain:, address:)
# бизнес-логика
end
end
Официальный gem загоняет SDK-обвязку в базовый класс:
class ApplicationTool < MCP::Tool
def self.call(**args, server_context: nil)
hash = payload(**args)
MCP::Tool::Response.new([{ type: "text", text: hash.to_json }])
end
end
class GetContractInfoTool < ApplicationTool
input_schema(
properties: {
chain: { type: "string" },
address: { type: "string" }
},
required: %w[chain address]
)
def self.payload(chain:, address:)
# бизнес-логика, возвращает Hash
end
end
Подклассы заняты только тем, какой Hash возвращает payload(**args); базовый класс заворачивает его в JSON-encoded text block внутри MCP::Tool::Response. Два плюса:
Tool.payload(chain:, address:), без распаковки protocol envelopeDSL dry-schema заменил на чистые литералы JSON Schema. Внешне это выглядит как шаг назад, но input_schema — ровно то, что клиент читает из MCP-спецификации напрямую, поэтому писать в виде примитива спецификации честнее.
Переезд тестов
433 теста, переключены с Tool.new.call(...) на Tool.payload(...). Все зелёные.
Синхронизация discovery / docs
.well-known/mcp.json с transport: "streamable-http", protocol_version: "2025-03-26"https://smarts.md/mcpclaude mcp add сменился с --transport sse на --transport httpmcp как primary, fast-mcp — как legacy/deprecatedПервая интеграция MCP заняла 30 минут. Переписывание — примерно столько же. Между ними — шесть дней.
Что до меня дошло: в молодой экосистеме вроде MCP gems отстают от спецификации — это норма. fast-mcp не плохой — мейнтейнер активен. Проблема в том, что сам протокол итерирует быстрее сторонних реализаций. Gem, который этот год выглядит надёжно, через полгода может оказаться «тем, что на старой спеке».
Если бы полгода назад я написал десять тысяч строк бизнес-логики поверх fast-mcp, сегодня я был бы прикован к старой спеке — потому что цена переписывания была бы такой, что я бы не решился её платить.
Но реально получилось так: слой fast-mcp с самого начала был тонкой обёрткой. Бизнес-логика жила в подклассах ApplicationTool. Поэтому реальная цена переписывания вышла такой:
Дорогая часть — это бизнес-логика. Обёртки протокола должны быть дешёвыми и переписываемыми. Самая большая услуга, которую Claude Code оказал мне шесть дней назад, в этом — он не утрамбовал бизнес-логику в callbacks fast-mcp. Он построил чистую абстракцию ApplicationTool, поглощающую SDK. Поэтому шесть дней спустя замена SDK ограничилась базовым классом и схемой.
То, что позволило мне за 6 дней пройти путь «первая интеграция → апгрейд спеки → переписывание», — это стабильность Claude как базовой модели. В каком именно agent-shell я сидел, имеет мало значения.
Исходники: https://github.com/defi-io/smarts