VectorMCP

Uma gem Ruby para construir servidores Model Context Protocol (MCP) que expõem ferramentas, recursos e prompts para clientes LLM.

Documentação

VectorMCP

Gem Version Docs Build Status Maintainability License: MIT

O VectorMCP é uma implementação em Ruby da especificação do lado do servidor do Model Context Protocol (MCP). Ele fornece uma estrutura para expor ferramentas, recursos, prompts, raízes, amostragem, middleware e segurança sobre o transporte HTTP streamable do MCP.

Destaques

  • HTTP streamable é o transporte integrado, com gerenciamento de sessão, retomabilidade e conformidade com MCP 2025-11-25
  • Ferramentas baseadas em classes via VectorMCP::Tool, além da API original baseada em blocos register_tool
  • Montagem em Rack e Rails através de server.rack_app
  • Autenticação e autorização opcionais, registro estruturado e hooks de middleware
  • Identidade com escopo de requisição: cada requisição é despachada por meio de sua própria invocação, então requisições concorrentes em uma mesma sessão nunca podem observar os cabeçalhos ou a autenticação umas das outras
  • Ferramentas/recursos/prompts com suporte a imagens, raízes e amostragem iniciada pelo servidor
  • Middleware de anonimização de campos baseado em tokens para manter valores sensíveis fora do contexto do LLM

Requisitos

  • Ruby 3.2+

Instalação

gem install vector_mcp
gem "vector_mcp"

Início Rápido

require "vector_mcp"

class Greet < VectorMCP::Tool
  description "Say hello to someone"
  param :name, type: :string, desc: "Name to greet", required: true

  def call(args, _session)
    "Hello, #{args["name"]}!"
  end
end

server = VectorMCP::Server.new(name: "MyApp", version: "1.0.0")
server.register(Greet)
server.run(port: 8080)

O DSL baseado em classes é opcional. A API existente baseada em blocos ainda funciona:

server.register_tool(
  name: "echo",
  description: "Echo back the supplied text",
  input_schema: {
    type: "object",
    properties: { text: { type: "string" } },
    required: ["text"]
  }
) { |args| args["text"] }

Rack e Rails

O VectorMCP pode ser executado como um servidor HTTP autônomo ou montado dentro de um aplicativo Rack existente:

require "vector_mcp"

server = VectorMCP::Server.new(name: "MyApp", version: "1.0.0")
server.register(Greet)

MCP_APP = server.rack_app

No Rails, monte-o em config/routes.rb:

mount MCP_APP => "/mcp"

Para ferramentas com suporte a ActiveRecord, opte por VectorMCP::Rails::Tool:

require "vector_mcp/rails/tool"

class FindUser < VectorMCP::Rails::Tool
  description "Find a user by id"
  param :id, type: :integer, required: true

  def call(args, _session)
    user = find!(User, args[:id])
    { id: user.id, email: user.email }
  end
end

Consulte docs/rails-setup-guide.md para um guia completo de configuração.

Ferramentas, Recursos e Prompts

Exponha ferramentas chamáveis:

server.register_tool(
  name: "calculate",
  description: "Performs basic math",
  input_schema: {
    type: "object",
    properties: {
      operation: { type: "string", enum: ["add", "subtract", "multiply"] },
      a: { type: "number" },
      b: { type: "number" }
    },
    required: ["operation", "a", "b"]
  }
) do |args|
  case args["operation"]
  when "add" then args["a"] + args["b"]
  when "subtract" then args["a"] - args["b"]
  when "multiply" then args["a"] * args["b"]
  end
end

Exponha recursos legíveis:

server.register_resource(
  uri: "file://config.json",
  name: "App Configuration",
  description: "Current application settings"
) { File.read("config.json") }

Defina modelos de prompt:

server.register_prompt(
  name: "code_review",
  description: "Reviews code for best practices",
  arguments: [
    { name: "language", description: "Programming language", required: true },
    { name: "code", description: "Code to review", required: true }
  ]
) do |args|
  {
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: "Review this #{args["language"]} code:\n\n#{args["code"]}"
      }
    }]
  }
end

VectorMCP::Tool também suporta type: :date e type: :datetime, que são validados como strings no JSON Schema e convertidos para Date e Time antes de #call ser executado.

Handlers que recebem um segundo argumento recebem a invocação por requisição, que expõe a identidade da sessão, os cabeçalhos/parâmetros da requisição e o usuário autenticado em um só lugar:

server.register_tool(
  name: "whoami",
  description: "Reports the caller's identity",
  input_schema: { type: "object", properties: {} }
) do |_args, invocation|
  {
    session: invocation.id,
    user: invocation.user,
    api_key_header: invocation.request_header("X-API-Key")
  }
end

Handlers de recursos recebem a mesma invocação como segundo argumento (ela também responde às consultas familiares user / authenticated? / can?, então handlers escritos para o argumento de contexto de segurança mais antigo continuam funcionando sem alterações).

Segurança e Middleware

O VectorMCP mantém a segurança como opcional, mas os primitivos são integrados:

server.enable_authentication!(
  strategy: :api_key,
  keys: [ENV.fetch("MCP_API_KEY")],
  rate_limit: { max_attempts: 10, window_seconds: 60 }
)

server.enable_authorization! do
  authorize_tools do |user, _action, tool|
    user[:role] == "admin" || !tool.name.start_with?("admin_")
  end
end

Autenticação personalizada também funciona:

server.enable_authentication!(strategy: :custom) do |request|
  api_key = request[:headers]["X-API-Key"]
  user = User.find_by(api_key: api_key)
  user ? { user_id: user.id, role: user.role } : false
end

Quando a autenticação está habilitada, o VectorMCP a aplica centralmente a handlers de requisição/notificação integrados e personalizados. Apenas initialize, ping e a notificação initialized são públicos; streams HTTP GET e requisições de exclusão de sessão também exigem credenciais.

Para implantações públicas, habilite a limitação de falhas de autenticação com rate_limit: true (10 tentativas por 60 segundos por padrão), ou forneça max_attempts, window_seconds e max_entries. Falhas repetidas são rastreadas por IP do cliente e uma impressão digital unidirecional de credenciais; requisições bloqueadas retornam HTTP 429, JSON-RPC -32029 e Retry-After. O limitador é em processo, então implantações multiprocesso ou distribuídas também devem impor um limite compartilhado no proxy ou gateway. Gere chaves de API com pelo menos 256 bits de entropia—por exemplo, ruby -rsecurerandom -e 'puts SecureRandom.hex(32)'—e carregue-as de um gerenciador de segredos ou variável de ambiente.

Para clientes MCP que falam OAuth 2.1 (por exemplo, Claude Desktop), passe um resource_metadata_url: para ativar a descoberta RFC 9728. Requisições não autenticadas para /mcp retornam 401 com um cabeçalho WWW-Authenticate apontando para o documento de metadados configurado, e o cliente conduz o restante do fluxo OAuth automaticamente. Consulte docs/oauth_resource_server.md para a referência de recursos e docs/rails_oauth_integration.md para uma receita completa de Rails + Doorkeeper.

O middleware pode se conectar a eventos de ferramenta, recurso, prompt, amostragem, autenticação e transporte, incluindo before_auth, after_auth, on_auth_error, before_request, after_response e on_transport_error.

Consulte security/README.md para o guia completo de segurança.

Anonimização de Campos

Mantenha valores de string sensíveis fora do contexto do LLM substituindo-os por tokens opacos estáveis. Os valores são tokenizados em resultados de ferramentas de saída e restaurados em argumentos de ferramentas de entrada, então o LLM vê apenas tokens enquanto seus handlers recebem os dados originais.

anonymizer = VectorMCP::Middleware::Anonymizer.new(
  store: VectorMCP::TokenStore.new,
  field_rules: [
    { pattern: /email/i, prefix: "EMAIL" },
    { pattern: /\bssn\b/i, prefix: "SSN" }
  ]
)
anonymizer.install_on(server)

Notas de Transporte

  • O VectorMCP vem com HTTP streamable como transporte integrado
  • POST /mcp aceita uma única requisição, notificação ou resposta JSON-RPC; arrays em lote são rejeitados
  • GET /mcp abre um stream SSE para mensagens iniciadas pelo servidor
  • DELETE /mcp encerra a sessão
  • O servidor anuncia o protocolo MCP 2025-11-25 e aceita cabeçalhos 2025-03-26 e 2024-11-05 para compatibilidade
  • Origens permitidas por padrão são restritas a localhost e endereços de loopback
  • Corpos POST são limitados a 16 MiB por padrão; configure max_body_bytes: em run ou rack_app quando necessário
  • Para aplicativos Rack montados, configure o mesmo limite no servidor web frontal ou proxy reverso para que requisições sejam rejeitadas antes do buffer do Rack

Inicialize uma sessão com curl:

curl -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'

Mais Recursos

  • Raízes via register_root e register_root_from_path
  • Recursos de imagem e ferramentas/prompts com suporte a imagens
  • Registro estruturado com loggers de componentes
  • Amostragem iniciada pelo servidor com suporte a streaming/chamada de ferramenta
  • Modelagem de requisições e observabilidade orientadas por middleware

Documentação

Contribuindo

Relatórios de bugs e pull requests são bem-vindos no GitHub.

Licença

Disponível como código aberto sob a Licença MIT.