Skip to content
Draft

Res mcp #1936

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0e26743
Gem skeleton for ruby_event_store-mcp
tomaszpatrzek Apr 27, 2026
4e5789e
Add MCP stdio server with JSON-RPC protocol handling
tomaszpatrzek Apr 27, 2026
fb290ff
Add stream_show tool
tomaszpatrzek Apr 27, 2026
bbfc5ab
Add stream_events tool with ReadEvents helper
tomaszpatrzek Apr 27, 2026
5f8392f
Add event_show tool
tomaszpatrzek Apr 27, 2026
54d31a3
Add event_streams tool
tomaszpatrzek Apr 27, 2026
f8fa152
Add search tool
tomaszpatrzek Apr 27, 2026
24da39e
Add stats tool
tomaszpatrzek Apr 27, 2026
ed5a10b
Add trace tool
tomaszpatrzek Apr 27, 2026
af4c1cb
Add bin/res-mcp entry point and wire all tools
tomaszpatrzek Apr 27, 2026
9099f59
Add README
tomaszpatrzek Apr 27, 2026
52a73f3
Add CI workflows
tomaszpatrzek Apr 27, 2026
acaa329
Add ruby_event_store-mcp to contrib Makefile
tomaszpatrzek Apr 27, 2026
b0104d2
Add integration spec
tomaszpatrzek Apr 27, 2026
02e8bc8
Improve mutation test coverage
tomaszpatrzek Apr 28, 2026
11f3369
Kill remaining testable mutations
tomaszpatrzek Apr 28, 2026
79a9c65
Kill mutants in event_show tool
tomaszpatrzek Apr 28, 2026
0094a18
Kill mutants in event_streams tool
tomaszpatrzek Apr 28, 2026
05f74b3
Kill mutants in search tool
tomaszpatrzek Apr 28, 2026
a36b6de
Kill mutants in stats tool
tomaszpatrzek Apr 28, 2026
0fbfa78
Kill mutants in stream_events tool
tomaszpatrzek Apr 28, 2026
9229c24
Kill mutants in stream_show tool
tomaszpatrzek Apr 28, 2026
b166f21
Refactor and kill mutants in trace tool
tomaszpatrzek Apr 30, 2026
c7b163b
Refactor trace tool rendering
tomaszpatrzek Apr 30, 2026
7a3a5b1
Kill mutants in Server class
tomaszpatrzek May 6, 2026
48089e8
Kill mutants in ReadEvents class
tomaszpatrzek May 6, 2026
89ce482
Add aggregate_history tool
tomaszpatrzek May 6, 2026
9ed62ea
Add recent tool and kill mutants in aggregate_history
tomaszpatrzek May 6, 2026
4e99f40
Update README: add aggregate_history and recent tools, align table
tomaszpatrzek May 6, 2026
854b09f
README: document config file locations for Claude Code and Claude Des…
tomaszpatrzek May 6, 2026
433ffba
Fix Claude Code MCP config instructions in res-mcp README
tomaszpatrzek Jun 11, 2026
3d8e3a7
Update ruby_event_store-mcp lockfile to ruby_event_store 2.19.2
tomaszpatrzek Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/ruby_event_store-mcp_mutate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: ruby_event_store-mcp_mutate
on:
workflow_dispatch:
repository_dispatch:
types:
- script
push:
branches:
- master
paths:
- contrib/ruby_event_store-mcp/**
- ".github/workflows/ruby_event_store-mcp_mutate.yml"
- support/**
- "!support/bundler/**"
- "!support/ci/**"
pull_request:
paths:
- contrib/ruby_event_store-mcp/**
- ".github/workflows/ruby_event_store-mcp_mutate.yml"
- support/**
- "!support/bundler/**"
- "!support/ci/**"
jobs:
mutate:
runs-on: macos-14
timeout-minutes: 120
env:
WORKING_DIRECTORY: contrib/ruby_event_store-mcp
RUBY_VERSION: "${{ matrix.ruby_version }}"
BUNDLE_GEMFILE: "${{ matrix.bundle_gemfile }}"
SINCE_SHA: "${{ github.event.pull_request.base.sha || 'HEAD~1' }}"
strategy:
fail-fast: false
matrix:
include:
- ruby_version: ruby-3.4
bundle_gemfile: Gemfile
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: test -e ${{ env.BUNDLE_GEMFILE }}.lock
working-directory: "${{ env.WORKING_DIRECTORY }}"
- uses: ruby/setup-ruby@v1
with:
ruby-version: "${{ env.RUBY_VERSION }}"
bundler-cache: true
working-directory: "${{ env.WORKING_DIRECTORY }}"
- run: make mutate-changes
working-directory: "${{ env.WORKING_DIRECTORY }}"
env:
RUBYOPT: "--enable-frozen-string-literal"
55 changes: 55 additions & 0 deletions .github/workflows/ruby_event_store-mcp_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: ruby_event_store-mcp_test
on:
workflow_dispatch:
repository_dispatch:
types:
- script
push:
branches:
- master
paths:
- contrib/ruby_event_store-mcp/**
- ".github/workflows/ruby_event_store-mcp_test.yml"
- support/**
- "!support/bundler/**"
- "!support/ci/**"
pull_request:
paths:
- contrib/ruby_event_store-mcp/**
- ".github/workflows/ruby_event_store-mcp_test.yml"
- support/**
- "!support/bundler/**"
- "!support/ci/**"
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 120
env:
WORKING_DIRECTORY: contrib/ruby_event_store-mcp
RUBY_VERSION: "${{ matrix.ruby_version }}"
BUNDLE_GEMFILE: "${{ matrix.bundle_gemfile }}"
strategy:
fail-fast: false
matrix:
include:
- ruby_version: ruby-3.4
bundle_gemfile: Gemfile
- ruby_version: ruby-3.3
bundle_gemfile: Gemfile
- ruby_version: ruby-3.2
bundle_gemfile: Gemfile
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- run: test -e ${{ env.BUNDLE_GEMFILE }}.lock
working-directory: "${{ env.WORKING_DIRECTORY }}"
- uses: ruby/setup-ruby@v1
with:
ruby-version: "${{ env.RUBY_VERSION }}"
bundler-cache: true
working-directory: "${{ env.WORKING_DIRECTORY }}"
- run: make test
working-directory: "${{ env.WORKING_DIRECTORY }}"
env:
RUBYOPT: "--enable-frozen-string-literal"
1 change: 1 addition & 0 deletions contrib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ GEMS = minitest-ruby_event_store \
ruby_event_store-sequel \
ruby_event_store-sidekiq_scheduler \
ruby_event_store-transformations \
ruby_event_store-mcp \
dres_rails \
dres_client

Expand Down
18 changes: 18 additions & 0 deletions contrib/ruby_event_store-mcp/.mutant.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# https://github.com/mbj/mutant/blob/master/docs/configuration.md

usage: opensource
requires:
- ruby_event_store/mcp
includes:
- lib
integration:
name: rspec
mutation:
operators: light
coverage_criteria:
process_abort: true
matcher:
subjects:
- RubyEventStore::MCP*
ignore:
- RubyEventStore::MCP::Server#start
7 changes: 7 additions & 0 deletions contrib/ruby_event_store-mcp/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

source "https://rubygems.org"
gemspec

eval_gemfile "../../support/bundler/Gemfile.shared"
gem "ruby_event_store", path: "../.."
101 changes: 101 additions & 0 deletions contrib/ruby_event_store-mcp/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
PATH
remote: ../..
specs:
ruby_event_store (2.19.2)
concurrent-ruby (~> 1.0, >= 1.1.6)

PATH
remote: .
specs:
ruby_event_store-mcp (0.1.0)
ruby_event_store (>= 1.0.0)

GEM
remote: https://rubygems.org/
specs:
ast (2.4.3)
concurrent-ruby (1.3.6)
date (3.5.1)
diff-lcs (1.6.2)
drb (2.2.3)
erb (6.0.4)
io-console (0.8.2)
irb (1.18.0)
pp (>= 0.6.0)
prism (>= 1.3.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
minitest (6.0.5)
drb (~> 2.0)
prism (~> 1.5)
mutant (0.15.1)
diff-lcs (>= 1.6, < 3)
irb (~> 1.15)
parser (~> 3.3.10)
regexp_parser (~> 2.10)
sorbet-runtime (~> 0.6.0)
unparser (~> 0.8.2)
mutant-minitest (0.15.1)
minitest (>= 5.11, < 7)
mutant (= 0.15.1)
mutex_m (~> 0.2)
mutant-rspec (0.15.1)
mutant (= 0.15.1)
rspec-core (>= 3.8.0, < 5.0.0)
mutex_m (0.3.0)
parser (3.3.11.1)
ast (~> 2.4.1)
racc
pp (0.6.3)
prettyprint
prettyprint (0.2.0)
prism (1.9.0)
psych (5.3.1)
date
stringio
racc (1.8.1)
rake (13.4.2)
rdoc (7.2.0)
erb
psych (>= 4.0.0)
tsort
regexp_parser (2.12.0)
reline (0.6.3)
io-console (~> 0.5)
rspec (3.13.2)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.6)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.8)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.7)
sorbet-runtime (0.6.13164)
stringio (3.2.0)
tsort (0.2.0)
unparser (0.8.2)
diff-lcs (>= 1.6, < 3)
parser (>= 3.3.0)
prism (>= 1.5.1)

PLATFORMS
arm64-darwin-23
ruby

DEPENDENCIES
irb
mutant
mutant-minitest
mutant-rspec
rake (>= 10.0)
rspec
ruby_event_store!
ruby_event_store-mcp!

BUNDLED WITH
2.7.1
8 changes: 8 additions & 0 deletions contrib/ruby_event_store-mcp/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
GEM_VERSION = $(shell cat lib/ruby_event_store/mcp/version.rb | grep VERSION | egrep -o '[0-9]+\.[0-9]+\.[0-9]+')
GEM_NAME = ruby_event_store-mcp

include ../../support/make/install.mk
include ../../support/make/test.mk
include ../../support/make/mutant.mk
include ../../support/make/gem.mk
include ../../support/make/help.mk
82 changes: 82 additions & 0 deletions contrib/ruby_event_store-mcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# ruby_event_store-mcp

Model Context Protocol (MCP) server for [RubyEventStore](https://railseventstore.org). Exposes your event store as AI tools so Claude (and other MCP clients) can inspect streams, events, and causal relationships directly.

## Installation

Add to your Rails app's `Gemfile`:

```ruby
gem "ruby_event_store-mcp"
```

## Usage

Run from the root of your Rails application:

```bash
bundle exec res-mcp
```

The server communicates over stdio using the MCP protocol. Installing the gem only provides the `res-mcp` binary — you still have to register it with your MCP client. Every client takes the same server definition; only the file it lives in differs.

### Claude Code

Add a `.mcp.json` to your project root (or run `claude mcp add res -- bundle exec res-mcp`):

```json
{
"mcpServers": {
"res": {
"command": "bundle",
"args": ["exec", "res-mcp"]
}
}
}
```

Launched from the project directory, Claude Code runs the server there, so no `cwd` is needed. On the next launch Claude Code asks you to trust the server — approve it, then run `/mcp` to confirm `res` is connected with its tools.

### Claude Desktop

Add the same block with an explicit `cwd` pointing at your app's root, in `claude_desktop_config.json`:

```json
{
"mcpServers": {
"res": {
"command": "bundle",
"args": ["exec", "res-mcp"],
"cwd": "/path/to/your/rails/app"
}
}
}
```

Config file locations:

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

### Other MCP clients

Cursor, Windsurf, Cline and others take the same `mcpServers` block in their own config file. VS Code's built-in MCP support uses a `servers` key with `"type": "stdio"` instead. The `bundle exec res-mcp` command is the portable part.

## Requirements

- Ruby >= 3.0
- A Rails application with `Rails.configuration.event_store` configured

## Available tools

| Tool | Description |
|---------------------|------------------------------------------------------------------|
| `recent` | Most recent events across all streams (default: 20, newest first)|
| `stream_show` | Event count, version, first/last event for a stream |
| `stream_events` | List events in a stream (filter by type, time range, limit) |
| `event_show` | Full event details: data, metadata, timestamps |
| `event_streams` | All streams an event has been published or linked to |
| `aggregate_history` | Full event history of an aggregate instance by type and ID |
| `search` | Search events by type, time range, or stream |
| `stats` | Total event count and unique event types |
| `trace` | Causation tree for all events sharing a correlation ID |
18 changes: 18 additions & 0 deletions contrib/ruby_event_store-mcp/bin/res-mcp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require_relative "../lib/ruby_event_store/mcp"

env_file = File.expand_path("config/environment.rb")

abort "Could not find config/environment.rb. Run `res-mcp` from the root of your Rails application." unless File.exist?(env_file)

require env_file

abort <<~MSG unless defined?(Rails) && Rails.configuration.respond_to?(:event_store)
Could not find event store instance after loading config/environment.rb.

Expected Rails.configuration.event_store to be set (standard RES setup).
MSG

RubyEventStore::MCP.server(Rails.configuration.event_store).start
32 changes: 32 additions & 0 deletions contrib/ruby_event_store-mcp/lib/ruby_event_store/mcp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require_relative "mcp/version"
require_relative "mcp/read_events"
require_relative "mcp/server"
require_relative "mcp/tools/stream_show"
require_relative "mcp/tools/stream_events"
require_relative "mcp/tools/event_show"
require_relative "mcp/tools/event_streams"
require_relative "mcp/tools/search"
require_relative "mcp/tools/stats"
require_relative "mcp/tools/trace"
require_relative "mcp/tools/aggregate_history"
require_relative "mcp/tools/recent"

module RubyEventStore
module MCP
def self.server(event_store)
Server
.new(event_store: event_store)
.register(Tools::StreamShow.new)
.register(Tools::StreamEvents.new)
.register(Tools::EventShow.new)
.register(Tools::EventStreams.new)
.register(Tools::Search.new)
.register(Tools::Stats.new)
.register(Tools::Trace.new)
.register(Tools::AggregateHistory.new)
.register(Tools::Recent.new)
end
end
end
Loading
Loading