Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 10 additions & 12 deletions .cursor/rules/release-changelogs.mdc
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
---
description: writing changelog markdown when cutting a new release of the gem
globs:
globs:
alwaysApply: false
---
- output the changelog as markdown when asked.
- output the changelog as markdown when asked.
- git tags are used to mark the commit that cut a new release of the gem
- the gem version is located in [version.rb](mdc:lib/model_context_protocol/version.rb)
- the gem version is located in [version.rb](mdc:lib/mcp/version.rb)
- use the git history, especially merge commits from PRs to construct the changelog
- when necessary, look at the diff of files changed to determine whether a PR should be listed in
- when necessary, look at the diff of files changed to determine whether a PR should be listed in
- ## Added; adds new functionality
- ## Changed; alters functionality; especially backward compatible changes
- ## Fixed; bugfixes that are forward compatible
- ## Fixed; bugfixes that are forward compatible

use the following format for changelogs:

https://cloudsmith.io/~shopify/repos/gems/packages/detail/ruby/mcp-ruby/{gem version}/

# Changelog

```
## Added
- New functionality added that was not present before

## Changed
- Alterations to functionality that may indicate breaking changes

## Fixed
- Bug fixes
- Bug fixes

#### Full change list:
- [Name of the PR #123](mdc:https:/github.com/Shopify/mcp-ruby/pull/123) @github-author-username
- [Name of the PR #456](mdc:https:/github.com/Shopify/mcp-ruby/pull/456) @another-github-author
- [Name of the PR #123](https:/github.com/modelcontextprotocol/ruby-sdk/pull/123) @github-author-username
- [Name of the PR #456](https:/github.com/modelcontextprotocol/ruby-sdk/pull/456) @another-github-author
```
100 changes: 59 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,29 @@

A Ruby gem for implementing Model Context Protocol servers

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'mcp'
```

And then execute:

```bash
$ bundle install
```

Or install it yourself as:

```bash
$ gem install mcp
```

## MCP Server

The `ModelContextProtocol::Server` class is the core component that handles JSON-RPC requests and responses.
The `MCP::Server` class is the core component that handles JSON-RPC requests and responses.
It implements the Model Context Protocol specification, handling model context requests and responses.

### Key Features
Expand Down Expand Up @@ -44,19 +64,17 @@ requests.
You can use the `Server#handle_json` method to handle requests.

```ruby
module ModelContextProtocol
class ApplicationController < ActionController::Base

def index
server = ModelContextProtocol::Server.new(
name: "my_server",
version: "1.0.0",
tools: [SomeTool, AnotherTool],
prompts: [MyPrompt],
server_context: { user_id: current_user.id },
)
render(json: server.handle_json(request.body.read).to_h)
end
class ApplicationController < ActionController::Base

def index
server = MCP::Server.new(
name: "my_server",
version: "1.0.0",
tools: [SomeTool, AnotherTool],
prompts: [MyPrompt],
server_context: { user_id: current_user.id },
)
render(json: server.handle_json(request.body.read))
end
end
```
Expand All @@ -67,11 +85,11 @@ If you want to build a local command-line application, you can use the stdio tra

```ruby
#!/usr/bin/env ruby
require "model_context_protocol"
require "model_context_protocol/transports/stdio"
require "mcp"
require "mcp/transports/stdio"

# Create a simple tool
class ExampleTool < ModelContextProtocol::Tool
class ExampleTool < MCP::Tool
description "A simple example tool that echoes back its arguments"
input_schema(
properties: {
Expand All @@ -82,7 +100,7 @@ class ExampleTool < ModelContextProtocol::Tool

class << self
def call(message:, server_context:)
ModelContextProtocol::Tool::Response.new([{
MCP::Tool::Response.new([{
type: "text",
text: "Hello from example tool! Message: #{message}",
}])
Expand All @@ -91,13 +109,13 @@ class ExampleTool < ModelContextProtocol::Tool
end

# Set up the server
server = ModelContextProtocol::Server.new(
server = MCP::Server.new(
name: "example_server",
tools: [ExampleTool],
)

# Create and start the transport
transport = ModelContextProtocol::Transports::StdioTransport.new(server)
transport = MCP::Transports::StdioTransport.new(server)
transport.open
```

Expand All @@ -112,10 +130,10 @@ $ ./stdio_server.rb

## Configuration

The gem can be configured using the `ModelContextProtocol.configure` block:
The gem can be configured using the `MCP.configure` block:

```ruby
ModelContextProtocol.configure do |config|
MCP.configure do |config|
config.exception_reporter = ->(exception, server_context) {
# Your exception reporting logic here
# For example with Bugsnag:
Expand All @@ -135,7 +153,7 @@ This is useful for systems where an application hosts more than one MCP server b
they might require different instrumentation callbacks.

```ruby
configuration = ModelContextProtocol::Configuration.new
configuration = MCP::Configuration.new
configuration.exception_reporter = ->(exception, server_context) {
# Your exception reporting logic here
# For example with Bugsnag:
Expand All @@ -148,7 +166,7 @@ configuration.instrumentation_callback = ->(data) {
puts "Got instrumentation data #{data.inspect}"
}

server = ModelContextProtocol::Server.new(
server = MCP::Server.new(
# ... all other options
configuration:,
)
Expand All @@ -167,7 +185,7 @@ server_context: { [String, Symbol] => Any }

**Example:**
```ruby
server = ModelContextProtocol::Server.new(
server = MCP::Server.new(
name: "my_server",
server_context: { user_id: current_user.id, request_id: request.uuid }
)
Expand Down Expand Up @@ -218,13 +236,13 @@ config.instrumentation_callback = ->(data) {
The server's protocol version can be overridden using the `protocol_version` class method:

```ruby
ModelContextProtocol::Server.protocol_version = "2024-11-05"
MCP::Server.protocol_version = "2024-11-05"
```

This will make all new server instances use the specified protocol version instead of the default version. The protocol version can be reset to the default by setting it to `nil`:

```ruby
ModelContextProtocol::Server.protocol_version = nil
MCP::Server.protocol_version = nil
```

Be sure to check the [MCP spec](https://spec.modelcontextprotocol.io/specification/2024-11-05/) for the protocol version to understand the supported features for the version being set.
Expand Down Expand Up @@ -253,12 +271,12 @@ If no exception reporter is configured, a default no-op reporter is used that si

MCP spec includes [Tools](https://modelcontextprotocol.io/docs/concepts/tools) which provide functionality to LLM apps.

This gem provides a `ModelContextProtocol::Tool` class that can be used to create tools in two ways:
This gem provides a `MCP::Tool` class that can be used to create tools in two ways:

1. As a class definition:

```ruby
class MyTool < ModelContextProtocol::Tool
class MyTool < MCP::Tool
description "This tool performs specific functionality..."
input_schema(
properties: {
Expand All @@ -275,17 +293,17 @@ class MyTool < ModelContextProtocol::Tool
)

def self.call(message:, server_context:)
Tool::Response.new([{ type: "text", text: "OK" }])
MCP::Tool::Response.new([{ type: "text", text: "OK" }])
end
end

tool = MyTool
```

2. By using the `ModelContextProtocol::Tool.define` method with a block:
2. By using the `MCP::Tool.define` method with a block:

```ruby
tool = ModelContextProtocol::Tool.define(
tool = MCP::Tool.define(
name: "my_tool",
description: "This tool performs specific functionality...",
annotations: {
Expand Down Expand Up @@ -316,12 +334,12 @@ Annotations can be set either through the class definition using the `annotation

MCP spec includes [Prompts](https://modelcontextprotocol.io/docs/concepts/prompts), which enable servers to define reusable prompt templates and workflows that clients can easily surface to users and LLMs.

The `ModelContextProtocol::Prompt` class provides two ways to create prompts:
The `MCP::Prompt` class provides two ways to create prompts:

1. As a class definition with metadata:

```ruby
class MyPrompt < ModelContextProtocol::Prompt
class MyPrompt < MCP::Prompt
prompt_name "my_prompt" # Optional - defaults to underscored class name
description "This prompt performs specific functionality..."
arguments [
Expand Down Expand Up @@ -354,10 +372,10 @@ end
prompt = MyPrompt
```

2. Using the `ModelContextProtocol::Prompt.define` method:
2. Using the `MCP::Prompt.define` method:

```ruby
prompt = ModelContextProtocol::Prompt.define(
prompt = MCP::Prompt.define(
name: "my_prompt",
description: "This prompt performs specific functionality...",
arguments: [
Expand Down Expand Up @@ -399,7 +417,7 @@ e.g. around authentication state or user preferences.
Register prompts with the MCP server:

```ruby
server = ModelContextProtocol::Server.new(
server = MCP::Server.new(
name: "my_server",
prompts: [MyPrompt],
server_context: { user_id: current_user.id },
Expand All @@ -417,7 +435,7 @@ The server allows registering a callback to receive information about instrument
To register a handler pass a proc/lambda to as `instrumentation_callback` into the server constructor.

```ruby
ModelContextProtocol.configure do |config|
MCP.configure do |config|
config.instrumentation_callback = ->(data) {
puts "Got instrumentation data #{data.inspect}"
end
Expand All @@ -439,16 +457,16 @@ This is to avoid potential issues with metric cardinality

MCP spec includes [Resources](https://modelcontextprotocol.io/docs/concepts/resources)

The `ModelContextProtocol::Resource` class provides a way to register resources with the server.
The `MCP::Resource` class provides a way to register resources with the server.

```ruby
resource = ModelContextProtocol::Resource.new(
resource = MCP::Resource.new(
uri: "example.com/my_resource",
mime_type: "text/plain",
text: "Lorem ipsum dolor sit amet"
)

server = ModelContextProtocol::Server.new(
server = MCP::Server.new(
name: "my_server",
resources: [resource],
)
Expand Down
4 changes: 2 additions & 2 deletions examples/stdio_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# frozen_string_literal: true

$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
require "model_context_protocol"
require "model_context_protocol/transports/stdio"
require "mcp"
require "mcp/transports/stdio"

# Create a simple tool
class ExampleTool < MCP::Tool
Expand Down
3 changes: 0 additions & 3 deletions lib/mcp-ruby.rb

This file was deleted.

41 changes: 41 additions & 0 deletions lib/mcp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

require_relative "mcp/server"
require_relative "mcp/string_utils"
require_relative "mcp/tool"
require_relative "mcp/tool/input_schema"
require_relative "mcp/tool/annotations"
require_relative "mcp/tool/response"
require_relative "mcp/content"
require_relative "mcp/resource"
require_relative "mcp/resource/contents"
require_relative "mcp/resource/embedded"
require_relative "mcp/resource_template"
require_relative "mcp/prompt"
require_relative "mcp/prompt/argument"
require_relative "mcp/prompt/message"
require_relative "mcp/prompt/result"
require_relative "mcp/version"
require_relative "mcp/configuration"
require_relative "mcp/methods"

module MCP
class << self
def configure
yield(configuration)
end

def configuration
@configuration ||= Configuration.new
end
end

class Annotations
attr_reader :audience, :priority

def initialize(audience: nil, priority: nil)
@audience = audience
@priority = priority
end
end
end
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

module ModelContextProtocol
module MCP
class Configuration
DEFAULT_PROTOCOL_VERSION = "2024-11-05"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# typed: true
# frozen_string_literal: true

module ModelContextProtocol
module MCP
module Content
class Text
attr_reader :text, :annotations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

module ModelContextProtocol
module MCP
module Instrumentation
def instrument_call(method, &block)
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

module ModelContextProtocol
module MCP
module Methods
INITIALIZE = "initialize"
PING = "ping"
Expand Down
Loading