VectorMCP

Una gema de Ruby para construir servidores del Protocolo de Contexto de Modelo (MCP) que exponen herramientas, recursos y avisos a clientes LLM.

Documentación

VectorMCP

Gem Version Docs Build Status Maintainability License: MIT

VectorMCP es una implementación en Ruby de la especificación del lado del servidor del Protocolo de Contexto de Modelos (MCP). Te proporciona un marco de trabajo para exponer herramientas, recursos, prompts, raíces, muestreo, middleware y seguridad a través del transporte HTTP transmisible de MCP.

Características destacadas

  • HTTP transmisible es el transporte integrado, con gestión de sesiones, capacidad de reanudación y cumplimiento con MCP 2025-11-25
  • Herramientas basadas en clases mediante VectorMCP::Tool, además de la API original basada en bloques register_tool
  • Montaje en Rack y Rails a través de server.rack_app
  • Autenticación y autorización opcionales, registro estructurado y enlaces de middleware
  • Identidad con ámbito de solicitud: cada solicitud se despacha a través de su propia invocación, por lo que las solicitudes concurrentes en una sesión nunca pueden observar los encabezados o la autenticación de las demás
  • Herramientas/recursos/prompts conscientes de imágenes, raíces y muestreo iniciado por el servidor
  • Middleware de anonimización de campos basado en tokens para mantener los valores sensibles fuera del contexto del LLM

Requisitos

  • Ruby 3.2+

Instalación

gem install vector_mcp
gem "vector_mcp"

Inicio 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)

El DSL basado en clases es opcional. La API existente basada en bloques sigue funcionando:

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 y Rails

VectorMCP puede ejecutarse como un servidor HTTP independiente o montarse dentro de una aplicación Rack existente:

require "vector_mcp"

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

MCP_APP = server.rack_app

En Rails, móntalo en config/routes.rb:

mount MCP_APP => "/mcp"

Para herramientas respaldadas por ActiveRecord, opta 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

Consulta docs/rails-setup-guide.md para obtener una guía completa de configuración.

Herramientas, Recursos y Prompts

Expón herramientas invocables:

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

Expón recursos legibles:

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

Define plantillas de prompts:

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 también admite type: :date y type: :datetime, que se validan como cadenas en JSON Schema y se convierten a Date y Time antes de que se ejecute #call.

Los manejadores que aceptan un segundo argumento reciben la invocación por solicitud, que expone la identidad de la sesión, los encabezados/parámetros de la solicitud y el usuario autenticado en un solo 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

Los manejadores de recursos reciben la misma invocación como segundo argumento (también responde a las consultas familiares user / authenticated? / can?, por lo que los manejadores escritos contra el argumento de contexto de seguridad anterior siguen funcionando sin cambios).

Seguridad y Middleware

VectorMCP mantiene la seguridad como opcional, pero los primitivos están 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

La autenticación personalizada también 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

Cuando la autenticación está habilitada, VectorMCP la aplica de forma centralizada a los manejadores de solicitudes/notificaciones integrados y personalizados. Solo initialize, ping y la notificación initialized son públicas; las transmisiones HTTP GET y las solicitudes de eliminación de sesión también requieren credenciales.

Para implementaciones públicas, habilita la limitación de fallos de autenticación con rate_limit: true (10 intentos por 60 segundos por defecto), o proporciona max_attempts, window_seconds y max_entries. Los fallos repetidos se rastrean por IP del cliente y una huella de credencial de un solo sentido; las solicitudes bloqueadas devuelven HTTP 429, JSON-RPC -32029 y Retry-After. El limitador está en proceso, por lo que las implementaciones multiproceso o distribuidas también deben aplicar un límite compartido en el proxy o la puerta de enlace. Genera claves API con al menos 256 bits de entropía—por ejemplo, ruby -rsecurerandom -e 'puts SecureRandom.hex(32)'—y cárgalas desde un gestor de secretos o una variable de entorno.

Para clientes MCP que hablan OAuth 2.1 (por ejemplo, Claude Desktop), pasa un resource_metadata_url: para activar el descubrimiento RFC 9728. Las solicitudes no autenticadas a /mcp devuelven 401 con un encabezado WWW-Authenticate que apunta al documento de metadatos configurado, y el cliente impulsa el resto del flujo OAuth automáticamente. Consulta docs/oauth_resource_server.md para la referencia de características y docs/rails_oauth_integration.md para una receta completa de Rails + Doorkeeper.

El middleware puede engancharse en eventos de herramientas, recursos, prompts, muestreo, autenticación y transporte, incluyendo before_auth, after_auth, on_auth_error, before_request, after_response y on_transport_error.

Consulta security/README.md para la guía completa de seguridad.

Anonimización de Campos

Mantén los valores de cadena sensibles fuera del contexto del LLM sustituyéndolos por tokens opacos estables. Los valores se tokenizan en los resultados de herramientas salientes y se restauran en los argumentos de herramientas entrantes, por lo que el LLM solo ve tokens mientras tus manejadores reciben los datos originales.

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

  • VectorMCP incluye HTTP transmisible como su transporte integrado
  • POST /mcp acepta una sola solicitud, notificación o respuesta JSON-RPC; los arreglos por lotes se rechazan
  • GET /mcp abre una transmisión SSE para mensajes iniciados por el servidor
  • DELETE /mcp termina la sesión
  • El servidor anuncia el protocolo MCP 2025-11-25 y acepta los encabezados 2025-03-26 y 2024-11-05 para compatibilidad
  • Los orígenes permitidos por defecto están restringidos a localhost y direcciones de bucle invertido
  • Los cuerpos POST están limitados a 16 MiB por defecto; configura max_body_bytes: en run o rack_app cuando sea necesario
  • Para aplicaciones Rack montadas, configura el mismo límite en el servidor web frontal o proxy inverso para que las solicitudes se rechacen antes del almacenamiento en búfer de Rack

Inicializa una sesión con 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"}}}'

Más Características

  • Raíces mediante register_root y register_root_from_path
  • Recursos de imagen y herramientas/prompts conscientes de imágenes
  • Registro estructurado con registradores de componentes
  • Muestreo iniciado por el servidor con soporte de transmisión/llamada a herramientas
  • Modelado de solicitudes y observabilidad impulsados por middleware

Documentación

Contribuciones

Los informes de errores y las solicitudes de extracción son bienvenidos en GitHub.

Licencia

Disponible como código abierto bajo la Licencia MIT.