Servidores MCP en Ruby


En este artículo te voy a explicar cómo implementar un servidor MCP en un proyecto Ruby con Sinatra.

Estas son las herramientas que vas a necesitar:

HerramientaDescripciónEnlace
Fast MCPGema Ruby para implementar un servidor MCP tanto en Rails como en cualquier solución Rack como Sinatrahttps://github.com/yjacquin/fast-mcp
MCP InspectorEs una herramienta que te permite explorar las tools y resources definidas en tu aplicaciónhttps://modelcontextprotocol.io/docs/tools/inspector
MCP RemotePermite configurar Cursor y Claude Desktop con un servidor MCP remoto, usando HTTP y Headers de autorización. Es la forma de tenerlo integrado.https://www.npmjs.com/package/mcp-remote

Fast MCP

Es una gema de Ruby que te permite crear un servidor MCP que puedas servir desde tu aplicación Rails o Sinatra.

El objetivo era crear una tool que permitiera interactuar con nuestro dominio de negocio. Queríamos ver cómo podíamos definirla y cómo podíamos hacer que se conectase con la base de datos. Tener una base a partir de la cual pudiéramos construir herramientas más complejas.

class FindCustomerTool < FastMcp::Tool
  description 'Busca un cliente por su NIF o pasaporte'
  arguments do
    required(:document_id).filled(:string).description('El NIF o pasaporte del cliente a buscar')
  end

  def call(document_id:)

    # Create the required services
    self.create_services

    # Find the customer
    customer = @customer_repository.first(conditions: {document_id: document_id})

    # Return the customer data
    if customer
      return { name: customer.name,
               surname: customer.surname,
               email: customer.email,
               phone: customer.phone_number}
    else
      return nil
    end

  end

  private

  def create_services
    @customer_repository = Repository::CustomerRepository.new
  end

end

Para poder publicar la tool necesitamos crear un servidor MCP, registrar la tool y configurar el middleware para la comunicación.

require 'fast_mcp'
# Crea el servidor MCP
mcp_server = FastMcp::Server.new(name: 'my-mcp-server',
                                 version: '1.0.0')
# Registra la tool
mcp_server.register_tool(::FindCustomerTool)
# Configura el middlware
use FastMcp::Transports::AuthenticatedRackTransport, mcp_server, {allowed_origins: [],
                                                                  localhost_only: false,
                                                                  auth_token: ENV['MCP_AUTH_TOKEN'],
                                                                  logger: Infraestructure::Logger.instance.logger}

¿Cómo funciona?

FastMcp habilita 2 rutas, GET /mcp/see y POST /mcp/messages.

GET /mcp/see

Utiliza HTTP SSE y permite responder a los clientes. Es un canal de sólo escritura, desde la aplicación se comunica con los clientes.

POST /mcp/messages

Permite descubrir las tools y los resources que ofrece el servidor MCP y hacer llamadas. Utiliza JSON RCP 2.0.

Con esta llamada, mensaje en el BODY, se pueden descubrir todas las rutas disponible.

{
  "jsonrpc": "2.0",
  "method": "tools/list",
  "id": "1"
}

Con esta llamada, mensaje en el BODY, se puede ejecutar la tool que hemos creado previamente

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {"name": "FindCustomerTool", "arguments": {"document_id": "55555555K"}},
  "id": "1"
}

Al hacer estas llamadas con Postman o con curl no recibiremos la respuesta, porque esta se envía a través de SSE por la ruta que se abrirá previamente en /mcp/see.

$ curl -v http://mbcanalysis.test/mcp/sse \
  -H "Authorization: 9oFRcMWZPo3JWFmDGC6r23209409f09s09HlgTGBHUeGsb"

$ curl -v http://mbcanalysis.test/mcp/sse \
  -H "Authorization: 9oFRcMWZPo3JWFmDGC6r23209409f09s09HlgTGBHUeGsb"
*   Trying 127.0.0.1:80...
* Connected to mbcanalysis.test (127.0.0.1) port 80 (#0)
> GET /mcp/sse HTTP/1.1
> Host: mbcanalysis.test
> User-Agent: curl/8.1.2
> Accept: */*
> Authorization: 9oFRcMWZPo3JWFmDGC6r23209409f09s09HlgTGBHUeGsb
>
< HTTP/1.1 200 OK
< Access-Control-Allow-Headers: Content-Type
< Access-Control-Allow-Methods: GET, OPTIONS
< Access-Control-Allow-Origin: *
< Access-Control-Max-Age: 86400
< Cache-Control: no-cache, no-store, must-revalidate
< Content-Type: text/event-stream
< Expires: 0
< Pragma: no-cache
< X-Accel-Buffering: no
< Date: Mon, 12 May 2025 12:13:07 GMT
< Transfer-Encoding: chunked
<
: SSE connection established

event: endpoint
data: /mcp/messages

Cuando hagamos la llamada a /tools/list desde Postman, observaremos en la sesión abierta de mcp/see lo siguiente:

: keep-alive 49

data: {"jsonrpc":"2.0","id":"1","result":{"tools":[{"name":"FindCustomerTool","description":"Busca un cliente por su NIF o pasaporte","inputSchema":{"type":"object","properties":{"document_id":{"type":"string","description":"El NIF o pasaporte del cliente a buscar"}},"required":["document_id"]}}]}}

: keep-alive 50

Conclusiones

La capa de transporte Rack, incluyendo la capa con autenticación, no está preparada para una aplicación en producción. Menos, con las características de nuestra aplicación. Creo que tendremos que escribir nuestra propia capa de transporte. La definición de tools … ha sido correcta.

MCP Inspector

Interactuar con Postman y con el terminal no es cómo. Hay una herramienta que nos permite interactuar con nuestro servidor MCP, listar y usar las tools. Puedes ejecutarlo usando npx sin la necesidad de instalarlo.

$ npx @modelcontextprotocol/inspector

Te has de conectar con una interfaz web desde la que podrás interactuar con servidores MCP, explorar los resources y las tools. Podrás ejecutar estas últimas facilitando los parámetros que defina. Incluso puedes configurar la autenticación con un header Authorization o API-KEY

Conexión autenticada con el servidor MCP

Exploración y ejecución de Tools

Integrar las Tools en Claude Desktop o Cursor

Una vez que has conseguido interactuar con tu servidor, viene cómo puedo integrar estas tools para que puedan usarlas agentes. Ahora veremos cómo integrarlas con Claude Desktop o con Cursor.

Si tu servidor MCP está disponible a través de HTTPS no podrás conectar desde Claude Desktop. Además, si tu servidor MCP usa headers para la autenticación, tampoco podrás conectar desde Cursor.

Para resolver este problema, tienes mcp-remote que actúa como un proxy y permite conectarse con tu servidor MCP.

Has de tener en cuenta que si usas nvm para gestionar las diferentes versiones de node, no puedes usar npx como se indicar en los ejemplos sino que has de instalar el paquete y referenciar directamente con la ruta.

Por lo tanto, necesitas instalar mcp-remote, conocer la ruta de node y del programa instalado.

Este comando te permite instalar mcp-remote de forma global

$ npm install -g mcp-remote

Este comando te permite conocer la ubicación de node

$ which node
/Users/jgil/.nvm/versions/node/v22.15.0/bin/node

Este comando te permite conocer la ubicación los paquetes instalados por npm

$ npm root -g
/Users/jgil/.nvm/versions/node/v22.15.0/lib/node_modules

Con esto podemos preparar este archivo que se usa en Cursor y Claude Desktop para configurar el servidor MCP. Ten en cuenta que has de reemplazar http://mbcanalysis.test/mcp/sse por la ruta en la que está disponible tu servidor mcp.

{
  "mcpServers": {    
    "mybooking": {
      "command": "/Users/jgil/.nvm/versions/node/v22.15.0/bin/node",
      "args": [
        "/Users/jgil/.nvm/versions/node/v22.15.0/lib/node_modules/mcp-remote/dist/proxy.js",
        "http://mbcanalysis.test/mcp/sse",
        "--allow-http",
        "--header",
        "Authorization:9oFRcMWZPo3JWFmDGC6r23209409f09s09HlgTGBHUeGsb"
      ]
    }
  }
}

Probando desde Claude Desktop

Una vez tengas configurado el servidor MCP puedes comprobar si se ha cargado correctamente y las tools que está disponibles.

Cuando hagas una pregunta el propio sistema comprobará si hay alguna tool que pueda dar respuesta a la pregunta y te preguntará si quieres usar una integración externa.

El sistema interactuará con la herramienta a través del servidor MCP para responder.

Problemas con Claude Desktop

No funcionan bien las tools con :: en su nombre. Si usas namespaces y tu herramienta es Mcp::Tool::FindCustomerTool no te funcionará. Dará un error al iniciar claude y el cliente no conectará con el servidor. Hemos de utilizar clases sin namespace. En Cursor no hay problema con ésto.

Referencias

Todos estos artículos me han ayudado a poder llevar a cabo este ejemplo