Skip to main content

Full Example

Production-ready Rails example with Stimulus controllers

Quick start

git clone https://github.com/WebMCP-org/examples.git
cd examples/rails && bundle install && bin/rails server

Installation

With importmap (Rails 7+):
bin/importmap pin @mcp-b/global
With npm/yarn:
npm install @mcp-b/global

The pattern

Use Stimulus connect/disconnect for lifecycle management:
import { Controller } from "@hotwired/stimulus"
import "@mcp-b/global"

export default class extends Controller {
  static values = { name: String, description: String }
  #registration = null

  connect() {
    if (!('modelContext' in navigator)) return

    this.#registration = navigator.modelContext.registerTool({
      name: this.nameValue,
      description: this.descriptionValue,
      inputSchema: { type: 'object', properties: {} },
      execute: async () => {
        return { content: [{ type: 'text', text: 'Done' }] }
      }
    })
  }

  disconnect() {
    this.#registration?.unregister()
  }
}
<div data-controller="webmcp"
     data-webmcp-name-value="my_tool"
     data-webmcp-description-value="My tool description">
  Content
</div>

Turbo persistence

Attach controllers to <body> for persistence across Turbo navigations:
<%# app/views/layouts/application.html.erb %>
<body data-controller="global-tools">
  <%= yield %>
</body>

Common issues

Include the CSRF token in fetch requests:
headers: {
  'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
}
Place controllers on <body> so they persist, or handle turbo:load event.
Pin the package: bin/importmap pin @mcp-b/global

Development

Use Chrome DevTools MCP for AI-driven development - your AI can write, discover, and test tools in real-time.