## Motivation and Context The README.md documents the "Rails (mount)" integration pattern, but the repository had no runnable Rails example. This adds a minimal Rails application under examples/rails that mounts StreamableHTTPTransport at /mcp and demonstrates class-based tools and a resource with a read handler. The MCP server is built in config/routes.rb rather than in an initializer because Zeitwerk sets up autoloading after initializers run, so the tool classes in app/tools cannot be referenced there. Code reloading is disabled because the transport holds the server built at boot. The app is booted with puma instead of rackup because rackup inserts `Rack::Lint` in its development default, which rejects the capitalized response header names that the transport currently emits. Closes #44. ## How Has This Been Tested? The curl walkthrough in examples/rails/README.md has been verified end to end against the running app: initialize and the initialized notification, tools/list, tools/call for both tools, resources/list, resources/read, the standalone SSE stream, and session termination via DELETE. ## Breaking Changes None. This only adds an example application and documentation links.
46 lines
1.7 KiB
Ruby
46 lines
1.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# The MCP server and its Streamable HTTP transport are built here, once at boot,
|
|
# following the "Rails (mount)" pattern from the top-level README.
|
|
# Routes are loaded after the application initializes, so the tool classes in app/tools
|
|
# are available at this point (they are not yet available in config/initializers,
|
|
# where Zeitwerk has not set up autoloading).
|
|
#
|
|
# The transport keeps session and SSE state in memory, so run a single server process
|
|
# (e.g., Puma with `workers 0`, which is the rackup default).
|
|
# See the top-level README for multi-instance and stateless deployment notes.
|
|
server = MCP::Server.new(
|
|
name: "rails_example_server",
|
|
title: "Rails Example Server",
|
|
version: "1.0.0",
|
|
tools: [AddTool, GreetingTool],
|
|
resources: [
|
|
MCP::Resource.new(
|
|
uri: "example://rails/readme",
|
|
name: "readme",
|
|
title: "Example README",
|
|
description: "Describes this Rails example MCP server",
|
|
mime_type: "text/plain",
|
|
),
|
|
],
|
|
server_context: { app_name: "mcp_rails_example" },
|
|
)
|
|
|
|
server.resources_read_handler do |params|
|
|
[
|
|
{
|
|
uri: params[:uri],
|
|
mimeType: "text/plain",
|
|
text: "This resource is served by the Rails example MCP server (Rails #{Rails.version}).",
|
|
},
|
|
]
|
|
end
|
|
|
|
transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)
|
|
|
|
Rails.application.routes.draw do
|
|
# `StreamableHTTPTransport` is a Rack app, so it can be mounted directly.
|
|
# `mount` directs all HTTP methods on /mcp to the transport, which internally dispatches POST (JSON-RPC messages),
|
|
# GET (SSE stream), and DELETE (session termination) per the MCP Streamable HTTP transport spec.
|
|
mount(transport => "/mcp")
|
|
end
|