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
58 changes: 58 additions & 0 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Ruby

on:
push:
branches:
- main
pull_request:

jobs:
test:
runs-on: ubuntu-latest
name: Ruby ${{ matrix.ruby }}
strategy:
matrix:
ruby: ['3.1.3', '3.2', '3.3', '3.4']

steps:
- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.16

- name: Install dependencies for typescript-mcp && pagination-server
run: |
cd spec/fixtures/typescript-mcp
bun install
cd ../pagination-server
bun install
cd ../../..

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true

- name: Install dependencies for gem and fast-mcp
run: |
bundle install
cd spec/fixtures/fast-mcp-ruby
bundle install
cd ../../..


- name: Generate random ports
id: portgen
run: |
echo "PORT1=$(( (RANDOM % 1000) + 3000 ))" >> $GITHUB_ENV
echo "PORT2=$(( (RANDOM % 1000) + 4000 ))" >> $GITHUB_ENV
echo "PORT3=$(( (RANDOM % 1000) + 5000 ))" >> $GITHUB_ENV

- name: Run the test suite
run: PORT1=$PORT1 PORT2=$PORT2 PORT3=$PORT3 bundle exec rake
env:
PORT1: ${{ env.PORT1 }}
PORT2: ${{ env.PORT2 }}
PORT3: ${{ env.PORT3 }}
29 changes: 0 additions & 29 deletions .github/workflows/main.yml

This file was deleted.

27 changes: 8 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,23 +229,6 @@ content = log_template.to_content(arguments: {
puts content
```

#### Resource Argument Completion

For resource templates, you can get suggested values for arguments:

```ruby
template = client.resource_template("user_profile")

# Search for possible values for a specific argument
suggestions = template.complete("username", "john")
puts "Suggested usernames:"
suggestions.values.each do |value|
puts "- #{value}"
end
puts "Total matches: #{suggestions.total}"
puts "Has more: #{suggestions.has_more}"
```

### Working with Prompts

MCP servers can provide predefined prompts that can be used in conversations:
Expand Down Expand Up @@ -308,7 +291,7 @@ response = chat.ask("Please review the recent commits using the checklist and su
puts response
```

## Argument Completion
### Argument Completion

Some MCP servers support argument completion for prompts and resource templates:

Expand All @@ -325,7 +308,13 @@ puts "Total matches: #{suggestions.total}"
puts "Has more results: #{suggestions.has_more}"
```

## Additional Chat Methods
### Pagination

MCP servers can support pagination for their lists. The client will automatically paginate the lists to include all items from the list you wanted to pull.

Pagination is supported for tools, resources, prompts, and resource templates.

### Additional Chat Methods

The gem extends RubyLLM's chat interface with convenient methods for MCP integration:

Expand Down
2 changes: 1 addition & 1 deletion examples/tools/streamable_mcp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
name: "streamable_mcp",
transport_type: :streamable,
config: {
url: "http://localhost:3005/mcp"
url: "http://localhost:#{ENV.fetch('PORT1', 3005)}/mcp"
}
)

Expand Down
42 changes: 29 additions & 13 deletions lib/ruby_llm/mcp/coordinator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def start_transport
@transport.set_protocol_version(@protocol_version)
end

@capabilities = RubyLLM::MCP::Capabilities.new(initialize_response.value["capabilities"])
@capabilities = RubyLLM::MCP::ServerCapabilities.new(initialize_response.value["capabilities"])
initialize_notification
end

Expand Down Expand Up @@ -98,11 +98,15 @@ def initialize_request
RubyLLM::MCP::Requests::Initialization.new(self).call
end

def tool_list
result = RubyLLM::MCP::Requests::ToolList.new(self).call
def tool_list(cursor: nil)
result = RubyLLM::MCP::Requests::ToolList.new(self, cursor: cursor).call
result.raise_error! if result.error?

result.value["tools"]
if result.next_cursor?
result.value["tools"] + tool_list(cursor: result.next_cursor)
else
result.value["tools"]
end
end

def execute_tool(**args)
Expand All @@ -125,33 +129,45 @@ def execute_tool(**args)
RubyLLM::MCP::Requests::ToolCall.new(self, **args).call
end

def resource_list
result = RubyLLM::MCP::Requests::ResourceList.new(self).call
def resource_list(cursor: nil)
result = RubyLLM::MCP::Requests::ResourceList.new(self, cursor: cursor).call
result.raise_error! if result.error?

result.value["resources"]
if result.next_cursor?
result.value["resources"] + resource_list(cursor: result.next_cursor)
else
result.value["resources"]
end
end

def resource_read(**args)
RubyLLM::MCP::Requests::ResourceRead.new(self, **args).call
end

def resource_template_list
result = RubyLLM::MCP::Requests::ResourceTemplateList.new(self).call
def resource_template_list(cursor: nil)
result = RubyLLM::MCP::Requests::ResourceTemplateList.new(self, cursor: cursor).call
result.raise_error! if result.error?

result.value["resourceTemplates"]
if result.next_cursor?
result.value["resourceTemplates"] + resource_template_list(cursor: result.next_cursor)
else
result.value["resourceTemplates"]
end
end

def resources_subscribe(**args)
RubyLLM::MCP::Requests::ResourcesSubscribe.new(self, **args).call
end

def prompt_list
result = RubyLLM::MCP::Requests::PromptList.new(self).call
def prompt_list(cursor: nil)
result = RubyLLM::MCP::Requests::PromptList.new(self, cursor: cursor).call
result.raise_error! if result.error?

result.value["prompts"]
if result.next_cursor?
result.value["prompts"] + prompt_list(cursor: result.next_cursor)
else
result.value["prompts"]
end
end

def execute_prompt(**args)
Expand Down
31 changes: 0 additions & 31 deletions lib/ruby_llm/mcp/requests/base.rb

This file was deleted.

12 changes: 8 additions & 4 deletions lib/ruby_llm/mcp/requests/initialization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
module RubyLLM
module MCP
module Requests
class Initialization < RubyLLM::MCP::Requests::Base
class Initialization
def initialize(coordinator)
@coordinator = coordinator
end

def call
coordinator.request(initialize_body)
@coordinator.request(initialize_body)
end

private
Expand All @@ -15,8 +19,8 @@ def initialize_body
jsonrpc: "2.0",
method: "initialize",
params: {
protocolVersion: coordinator.protocol_version,
capabilities: coordinator.client_capabilities,
protocolVersion: @coordinator.protocol_version,
capabilities: @coordinator.client_capabilities,
clientInfo: {
name: "RubyLLM-MCP Client",
version: RubyLLM::MCP::VERSION
Expand Down
30 changes: 0 additions & 30 deletions lib/ruby_llm/mcp/requests/meta.rb

This file was deleted.

8 changes: 6 additions & 2 deletions lib/ruby_llm/mcp/requests/ping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
module RubyLLM
module MCP
module Requests
class Ping < Base
class Ping
def initialize(coordinator)
@coordinator = coordinator
end

def call
coordinator.request(ping_body)
@coordinator.request(ping_body)
end

def ping_body
Expand Down
12 changes: 10 additions & 2 deletions lib/ruby_llm/mcp/requests/prompt_list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@
module RubyLLM
module MCP
module Requests
class PromptList < Base
class PromptList
include Shared::Pagination

def initialize(coordinator, cursor: nil)
@coordinator = coordinator
@cursor = cursor
end

def call
coordinator.request(request_body)
body = merge_pagination(request_body)
@coordinator.request(body)
end

private
Expand Down
14 changes: 12 additions & 2 deletions lib/ruby_llm/mcp/requests/resource_list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,21 @@
module RubyLLM
module MCP
module Requests
class ResourceList < Base
class ResourceList
include Shared::Pagination

def initialize(coordinator, cursor: nil)
@coordinator = coordinator
@cursor = cursor
end

def call
coordinator.request(resource_list_body)
body = merge_pagination(resource_list_body)
@coordinator.request(body)
end

private

def resource_list_body
{
jsonrpc: "2.0",
Expand Down
Loading