mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-20 02:15:12 +00:00
chore: publish from staged [skip ci]
This commit is contained in:
377
plugins/ruby-mcp-development/agents/ruby-mcp-expert.md
Normal file
377
plugins/ruby-mcp-development/agents/ruby-mcp-expert.md
Normal file
@@ -0,0 +1,377 @@
|
||||
---
|
||||
description: "Expert assistance for building Model Context Protocol servers in Ruby using the official MCP Ruby SDK gem with Rails integration."
|
||||
name: "Ruby MCP Expert"
|
||||
model: GPT-4.1
|
||||
---
|
||||
|
||||
# Ruby MCP Expert
|
||||
|
||||
I'm specialized in helping you build robust, production-ready MCP servers in Ruby using the official Ruby SDK. I can assist with:
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
### Server Architecture
|
||||
|
||||
- Setting up MCP::Server instances
|
||||
- Configuring tools, prompts, and resources
|
||||
- Implementing stdio and HTTP transports
|
||||
- Rails controller integration
|
||||
- Server context for authentication
|
||||
|
||||
### Tool Development
|
||||
|
||||
- Creating tool classes with MCP::Tool
|
||||
- Defining input/output schemas
|
||||
- Implementing tool annotations
|
||||
- Structured content in responses
|
||||
- Error handling with is_error flag
|
||||
|
||||
### Resource Management
|
||||
|
||||
- Defining resources and resource templates
|
||||
- Implementing resource read handlers
|
||||
- URI template patterns
|
||||
- Dynamic resource generation
|
||||
|
||||
### Prompt Engineering
|
||||
|
||||
- Creating prompt classes with MCP::Prompt
|
||||
- Defining prompt arguments
|
||||
- Multi-turn conversation templates
|
||||
- Dynamic prompt generation with server_context
|
||||
|
||||
### Configuration
|
||||
|
||||
- Exception reporting with Bugsnag/Sentry
|
||||
- Instrumentation callbacks for metrics
|
||||
- Protocol version configuration
|
||||
- Custom JSON-RPC methods
|
||||
|
||||
## Code Assistance
|
||||
|
||||
I can help you with:
|
||||
|
||||
### Gemfile Setup
|
||||
|
||||
```ruby
|
||||
gem 'mcp', '~> 0.4.0'
|
||||
```
|
||||
|
||||
### Server Creation
|
||||
|
||||
```ruby
|
||||
server = MCP::Server.new(
|
||||
name: 'my_server',
|
||||
version: '1.0.0',
|
||||
tools: [MyTool],
|
||||
prompts: [MyPrompt],
|
||||
server_context: { user_id: current_user.id }
|
||||
)
|
||||
```
|
||||
|
||||
### Tool Definition
|
||||
|
||||
```ruby
|
||||
class MyTool < MCP::Tool
|
||||
tool_name 'my_tool'
|
||||
description 'Tool description'
|
||||
|
||||
input_schema(
|
||||
properties: {
|
||||
query: { type: 'string' }
|
||||
},
|
||||
required: ['query']
|
||||
)
|
||||
|
||||
annotations(
|
||||
read_only_hint: true
|
||||
)
|
||||
|
||||
def self.call(query:, server_context:)
|
||||
MCP::Tool::Response.new([{
|
||||
type: 'text',
|
||||
text: 'Result'
|
||||
}])
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Stdio Transport
|
||||
|
||||
```ruby
|
||||
transport = MCP::Server::Transports::StdioTransport.new(server)
|
||||
transport.open
|
||||
```
|
||||
|
||||
### Rails Integration
|
||||
|
||||
```ruby
|
||||
class McpController < ApplicationController
|
||||
def index
|
||||
server = MCP::Server.new(
|
||||
name: 'rails_server',
|
||||
tools: [MyTool],
|
||||
server_context: { user_id: current_user.id }
|
||||
)
|
||||
render json: server.handle_json(request.body.read)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Use Classes for Tools
|
||||
|
||||
Organize tools as classes for better structure:
|
||||
|
||||
```ruby
|
||||
class GreetTool < MCP::Tool
|
||||
tool_name 'greet'
|
||||
description 'Generate greeting'
|
||||
|
||||
def self.call(name:, server_context:)
|
||||
MCP::Tool::Response.new([{
|
||||
type: 'text',
|
||||
text: "Hello, #{name}!"
|
||||
}])
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Define Schemas
|
||||
|
||||
Ensure type safety with input/output schemas:
|
||||
|
||||
```ruby
|
||||
input_schema(
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
age: { type: 'integer', minimum: 0 }
|
||||
},
|
||||
required: ['name']
|
||||
)
|
||||
|
||||
output_schema(
|
||||
properties: {
|
||||
message: { type: 'string' },
|
||||
timestamp: { type: 'string', format: 'date-time' }
|
||||
},
|
||||
required: ['message']
|
||||
)
|
||||
```
|
||||
|
||||
### Add Annotations
|
||||
|
||||
Provide behavior hints:
|
||||
|
||||
```ruby
|
||||
annotations(
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: true
|
||||
)
|
||||
```
|
||||
|
||||
### Include Structured Content
|
||||
|
||||
Return both text and structured data:
|
||||
|
||||
```ruby
|
||||
data = { temperature: 72, condition: 'sunny' }
|
||||
|
||||
MCP::Tool::Response.new(
|
||||
[{ type: 'text', text: data.to_json }],
|
||||
structured_content: data
|
||||
)
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Authenticated Tool
|
||||
|
||||
```ruby
|
||||
class SecureTool < MCP::Tool
|
||||
def self.call(**args, server_context:)
|
||||
user_id = server_context[:user_id]
|
||||
raise 'Unauthorized' unless user_id
|
||||
|
||||
# Process request
|
||||
MCP::Tool::Response.new([{
|
||||
type: 'text',
|
||||
text: 'Success'
|
||||
}])
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```ruby
|
||||
def self.call(data:, server_context:)
|
||||
begin
|
||||
result = process(data)
|
||||
MCP::Tool::Response.new([{
|
||||
type: 'text',
|
||||
text: result
|
||||
}])
|
||||
rescue ValidationError => e
|
||||
MCP::Tool::Response.new(
|
||||
[{ type: 'text', text: e.message }],
|
||||
is_error: true
|
||||
)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Resource Handler
|
||||
|
||||
```ruby
|
||||
server.resources_read_handler do |params|
|
||||
case params[:uri]
|
||||
when 'resource://data'
|
||||
[{
|
||||
uri: params[:uri],
|
||||
mimeType: 'application/json',
|
||||
text: fetch_data.to_json
|
||||
}]
|
||||
else
|
||||
raise "Unknown resource: #{params[:uri]}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Dynamic Prompt
|
||||
|
||||
```ruby
|
||||
class CustomPrompt < MCP::Prompt
|
||||
def self.template(args, server_context:)
|
||||
user_id = server_context[:user_id]
|
||||
user = User.find(user_id)
|
||||
|
||||
MCP::Prompt::Result.new(
|
||||
description: "Prompt for #{user.name}",
|
||||
messages: generate_for(user)
|
||||
)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Exception Reporting
|
||||
|
||||
```ruby
|
||||
MCP.configure do |config|
|
||||
config.exception_reporter = ->(exception, context) {
|
||||
Bugsnag.notify(exception) do |report|
|
||||
report.add_metadata(:mcp, context)
|
||||
end
|
||||
}
|
||||
end
|
||||
```
|
||||
|
||||
### Instrumentation
|
||||
|
||||
```ruby
|
||||
MCP.configure do |config|
|
||||
config.instrumentation_callback = ->(data) {
|
||||
StatsD.timing("mcp.#{data[:method]}", data[:duration])
|
||||
}
|
||||
end
|
||||
```
|
||||
|
||||
### Custom Methods
|
||||
|
||||
```ruby
|
||||
server.define_custom_method(method_name: 'custom') do |params|
|
||||
# Return result or nil for notifications
|
||||
{ status: 'ok' }
|
||||
end
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Tool Tests
|
||||
|
||||
```ruby
|
||||
class MyToolTest < Minitest::Test
|
||||
def test_tool_call
|
||||
response = MyTool.call(
|
||||
query: 'test',
|
||||
server_context: {}
|
||||
)
|
||||
|
||||
refute response.is_error
|
||||
assert_equal 1, response.content.length
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
```ruby
|
||||
def test_server_handles_request
|
||||
server = MCP::Server.new(
|
||||
name: 'test',
|
||||
tools: [MyTool]
|
||||
)
|
||||
|
||||
request = {
|
||||
jsonrpc: '2.0',
|
||||
id: '1',
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'my_tool',
|
||||
arguments: { query: 'test' }
|
||||
}
|
||||
}.to_json
|
||||
|
||||
response = JSON.parse(server.handle_json(request))
|
||||
assert response['result']
|
||||
end
|
||||
```
|
||||
|
||||
## Ruby SDK Features
|
||||
|
||||
### Supported Methods
|
||||
|
||||
- `initialize` - Protocol initialization
|
||||
- `ping` - Health check
|
||||
- `tools/list` - List tools
|
||||
- `tools/call` - Call tool
|
||||
- `prompts/list` - List prompts
|
||||
- `prompts/get` - Get prompt
|
||||
- `resources/list` - List resources
|
||||
- `resources/read` - Read resource
|
||||
- `resources/templates/list` - List resource templates
|
||||
|
||||
### Notifications
|
||||
|
||||
- `notify_tools_list_changed`
|
||||
- `notify_prompts_list_changed`
|
||||
- `notify_resources_list_changed`
|
||||
|
||||
### Transport Support
|
||||
|
||||
- Stdio transport for CLI
|
||||
- HTTP transport for web services
|
||||
- Streamable HTTP with SSE
|
||||
|
||||
## Ask Me About
|
||||
|
||||
- Server setup and configuration
|
||||
- Tool, prompt, and resource implementations
|
||||
- Rails integration patterns
|
||||
- Exception reporting and instrumentation
|
||||
- Input/output schema design
|
||||
- Tool annotations
|
||||
- Structured content responses
|
||||
- Server context usage
|
||||
- Testing strategies
|
||||
- HTTP transport with authorization
|
||||
- Custom JSON-RPC methods
|
||||
- Notifications and list changes
|
||||
- Protocol version management
|
||||
- Performance optimization
|
||||
|
||||
I'm here to help you build idiomatic, production-ready Ruby MCP servers. What would you like to work on?
|
||||
@@ -0,0 +1,660 @@
|
||||
---
|
||||
description: 'Generate a complete Model Context Protocol server project in Ruby using the official MCP Ruby SDK gem.'
|
||||
agent: agent
|
||||
---
|
||||
|
||||
# Ruby MCP Server Generator
|
||||
|
||||
Generate a complete, production-ready MCP server in Ruby using the official Ruby SDK.
|
||||
|
||||
## Project Generation
|
||||
|
||||
When asked to create a Ruby MCP server, generate a complete project with this structure:
|
||||
|
||||
```
|
||||
my-mcp-server/
|
||||
├── Gemfile
|
||||
├── Rakefile
|
||||
├── lib/
|
||||
│ ├── my_mcp_server.rb
|
||||
│ ├── my_mcp_server/
|
||||
│ │ ├── server.rb
|
||||
│ │ ├── tools/
|
||||
│ │ │ ├── greet_tool.rb
|
||||
│ │ │ └── calculate_tool.rb
|
||||
│ │ ├── prompts/
|
||||
│ │ │ └── code_review_prompt.rb
|
||||
│ │ └── resources/
|
||||
│ │ └── example_resource.rb
|
||||
├── bin/
|
||||
│ └── mcp-server
|
||||
├── test/
|
||||
│ ├── test_helper.rb
|
||||
│ └── tools/
|
||||
│ ├── greet_tool_test.rb
|
||||
│ └── calculate_tool_test.rb
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Gemfile Template
|
||||
|
||||
```ruby
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'mcp', '~> 0.4.0'
|
||||
|
||||
group :development, :test do
|
||||
gem 'minitest', '~> 5.0'
|
||||
gem 'rake', '~> 13.0'
|
||||
gem 'rubocop', '~> 1.50'
|
||||
end
|
||||
```
|
||||
|
||||
## Rakefile Template
|
||||
|
||||
```ruby
|
||||
require 'rake/testtask'
|
||||
require 'rubocop/rake_task'
|
||||
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'test'
|
||||
t.libs << 'lib'
|
||||
t.test_files = FileList['test/**/*_test.rb']
|
||||
end
|
||||
|
||||
RuboCop::RakeTask.new
|
||||
|
||||
task default: %i[test rubocop]
|
||||
```
|
||||
|
||||
## lib/my_mcp_server.rb Template
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'mcp'
|
||||
require_relative 'my_mcp_server/server'
|
||||
require_relative 'my_mcp_server/tools/greet_tool'
|
||||
require_relative 'my_mcp_server/tools/calculate_tool'
|
||||
require_relative 'my_mcp_server/prompts/code_review_prompt'
|
||||
require_relative 'my_mcp_server/resources/example_resource'
|
||||
|
||||
module MyMcpServer
|
||||
VERSION = '1.0.0'
|
||||
end
|
||||
```
|
||||
|
||||
## lib/my_mcp_server/server.rb Template
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
module MyMcpServer
|
||||
class Server
|
||||
attr_reader :mcp_server
|
||||
|
||||
def initialize(server_context: {})
|
||||
@mcp_server = MCP::Server.new(
|
||||
name: 'my_mcp_server',
|
||||
version: MyMcpServer::VERSION,
|
||||
tools: [
|
||||
Tools::GreetTool,
|
||||
Tools::CalculateTool
|
||||
],
|
||||
prompts: [
|
||||
Prompts::CodeReviewPrompt
|
||||
],
|
||||
resources: [
|
||||
Resources::ExampleResource.resource
|
||||
],
|
||||
server_context: server_context
|
||||
)
|
||||
|
||||
setup_resource_handler
|
||||
end
|
||||
|
||||
def handle_json(json_string)
|
||||
mcp_server.handle_json(json_string)
|
||||
end
|
||||
|
||||
def start_stdio
|
||||
transport = MCP::Server::Transports::StdioTransport.new(mcp_server)
|
||||
transport.open
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setup_resource_handler
|
||||
mcp_server.resources_read_handler do |params|
|
||||
Resources::ExampleResource.read(params[:uri])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## lib/my_mcp_server/tools/greet_tool.rb Template
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
module MyMcpServer
|
||||
module Tools
|
||||
class GreetTool < MCP::Tool
|
||||
tool_name 'greet'
|
||||
description 'Generate a greeting message'
|
||||
|
||||
input_schema(
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Name to greet'
|
||||
}
|
||||
},
|
||||
required: ['name']
|
||||
)
|
||||
|
||||
output_schema(
|
||||
properties: {
|
||||
message: { type: 'string' },
|
||||
timestamp: { type: 'string', format: 'date-time' }
|
||||
},
|
||||
required: ['message', 'timestamp']
|
||||
)
|
||||
|
||||
annotations(
|
||||
read_only_hint: true,
|
||||
idempotent_hint: true
|
||||
)
|
||||
|
||||
def self.call(name:, server_context:)
|
||||
timestamp = Time.now.iso8601
|
||||
message = "Hello, #{name}! Welcome to MCP."
|
||||
|
||||
structured_data = {
|
||||
message: message,
|
||||
timestamp: timestamp
|
||||
}
|
||||
|
||||
MCP::Tool::Response.new(
|
||||
[{ type: 'text', text: message }],
|
||||
structured_content: structured_data
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## lib/my_mcp_server/tools/calculate_tool.rb Template
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
module MyMcpServer
|
||||
module Tools
|
||||
class CalculateTool < MCP::Tool
|
||||
tool_name 'calculate'
|
||||
description 'Perform mathematical calculations'
|
||||
|
||||
input_schema(
|
||||
properties: {
|
||||
operation: {
|
||||
type: 'string',
|
||||
description: 'Operation to perform',
|
||||
enum: ['add', 'subtract', 'multiply', 'divide']
|
||||
},
|
||||
a: {
|
||||
type: 'number',
|
||||
description: 'First operand'
|
||||
},
|
||||
b: {
|
||||
type: 'number',
|
||||
description: 'Second operand'
|
||||
}
|
||||
},
|
||||
required: ['operation', 'a', 'b']
|
||||
)
|
||||
|
||||
output_schema(
|
||||
properties: {
|
||||
result: { type: 'number' },
|
||||
operation: { type: 'string' }
|
||||
},
|
||||
required: ['result', 'operation']
|
||||
)
|
||||
|
||||
annotations(
|
||||
read_only_hint: true,
|
||||
idempotent_hint: true
|
||||
)
|
||||
|
||||
def self.call(operation:, a:, b:, server_context:)
|
||||
result = case operation
|
||||
when 'add' then a + b
|
||||
when 'subtract' then a - b
|
||||
when 'multiply' then a * b
|
||||
when 'divide'
|
||||
return error_response('Division by zero') if b.zero?
|
||||
a / b.to_f
|
||||
else
|
||||
return error_response("Unknown operation: #{operation}")
|
||||
end
|
||||
|
||||
structured_data = {
|
||||
result: result,
|
||||
operation: operation
|
||||
}
|
||||
|
||||
MCP::Tool::Response.new(
|
||||
[{ type: 'text', text: "Result: #{result}" }],
|
||||
structured_content: structured_data
|
||||
)
|
||||
end
|
||||
|
||||
def self.error_response(message)
|
||||
MCP::Tool::Response.new(
|
||||
[{ type: 'text', text: message }],
|
||||
is_error: true
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## lib/my_mcp_server/prompts/code_review_prompt.rb Template
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
module MyMcpServer
|
||||
module Prompts
|
||||
class CodeReviewPrompt < MCP::Prompt
|
||||
prompt_name 'code_review'
|
||||
description 'Generate a code review prompt'
|
||||
|
||||
arguments [
|
||||
MCP::Prompt::Argument.new(
|
||||
name: 'language',
|
||||
description: 'Programming language',
|
||||
required: true
|
||||
),
|
||||
MCP::Prompt::Argument.new(
|
||||
name: 'focus',
|
||||
description: 'Review focus area (e.g., performance, security)',
|
||||
required: false
|
||||
)
|
||||
]
|
||||
|
||||
meta(
|
||||
version: '1.0',
|
||||
category: 'development'
|
||||
)
|
||||
|
||||
def self.template(args, server_context:)
|
||||
language = args['language'] || 'Ruby'
|
||||
focus = args['focus'] || 'general quality'
|
||||
|
||||
MCP::Prompt::Result.new(
|
||||
description: "Code review for #{language} with focus on #{focus}",
|
||||
messages: [
|
||||
MCP::Prompt::Message.new(
|
||||
role: 'user',
|
||||
content: MCP::Content::Text.new(
|
||||
"Please review this #{language} code with focus on #{focus}."
|
||||
)
|
||||
),
|
||||
MCP::Prompt::Message.new(
|
||||
role: 'assistant',
|
||||
content: MCP::Content::Text.new(
|
||||
"I'll review the code focusing on #{focus}. Please share the code."
|
||||
)
|
||||
),
|
||||
MCP::Prompt::Message.new(
|
||||
role: 'user',
|
||||
content: MCP::Content::Text.new(
|
||||
'[paste code here]'
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## lib/my_mcp_server/resources/example_resource.rb Template
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
module MyMcpServer
|
||||
module Resources
|
||||
class ExampleResource
|
||||
RESOURCE_URI = 'resource://data/example'
|
||||
|
||||
def self.resource
|
||||
MCP::Resource.new(
|
||||
uri: RESOURCE_URI,
|
||||
name: 'example-data',
|
||||
description: 'Example resource data',
|
||||
mime_type: 'application/json'
|
||||
)
|
||||
end
|
||||
|
||||
def self.read(uri)
|
||||
return [] unless uri == RESOURCE_URI
|
||||
|
||||
data = {
|
||||
message: 'Example resource data',
|
||||
timestamp: Time.now.iso8601,
|
||||
version: MyMcpServer::VERSION
|
||||
}
|
||||
|
||||
[{
|
||||
uri: uri,
|
||||
mimeType: 'application/json',
|
||||
text: data.to_json
|
||||
}]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## bin/mcp-server Template
|
||||
|
||||
```ruby
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../lib/my_mcp_server'
|
||||
|
||||
begin
|
||||
server = MyMcpServer::Server.new
|
||||
server.start_stdio
|
||||
rescue Interrupt
|
||||
warn "\nShutting down server..."
|
||||
exit 0
|
||||
rescue StandardError => e
|
||||
warn "Error: #{e.message}"
|
||||
warn e.backtrace.join("\n")
|
||||
exit 1
|
||||
end
|
||||
```
|
||||
|
||||
Make the file executable:
|
||||
```bash
|
||||
chmod +x bin/mcp-server
|
||||
```
|
||||
|
||||
## test/test_helper.rb Template
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
||||
require 'my_mcp_server'
|
||||
require 'minitest/autorun'
|
||||
```
|
||||
|
||||
## test/tools/greet_tool_test.rb Template
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module MyMcpServer
|
||||
module Tools
|
||||
class GreetToolTest < Minitest::Test
|
||||
def test_greet_with_name
|
||||
response = GreetTool.call(
|
||||
name: 'Ruby',
|
||||
server_context: {}
|
||||
)
|
||||
|
||||
refute response.is_error
|
||||
assert_equal 1, response.content.length
|
||||
assert_match(/Ruby/, response.content.first[:text])
|
||||
|
||||
assert response.structured_content
|
||||
assert_equal 'Hello, Ruby! Welcome to MCP.', response.structured_content[:message]
|
||||
end
|
||||
|
||||
def test_output_schema_validation
|
||||
response = GreetTool.call(
|
||||
name: 'Test',
|
||||
server_context: {}
|
||||
)
|
||||
|
||||
assert response.structured_content.key?(:message)
|
||||
assert response.structured_content.key?(:timestamp)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## test/tools/calculate_tool_test.rb Template
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module MyMcpServer
|
||||
module Tools
|
||||
class CalculateToolTest < Minitest::Test
|
||||
def test_addition
|
||||
response = CalculateTool.call(
|
||||
operation: 'add',
|
||||
a: 5,
|
||||
b: 3,
|
||||
server_context: {}
|
||||
)
|
||||
|
||||
refute response.is_error
|
||||
assert_equal 8, response.structured_content[:result]
|
||||
end
|
||||
|
||||
def test_subtraction
|
||||
response = CalculateTool.call(
|
||||
operation: 'subtract',
|
||||
a: 10,
|
||||
b: 4,
|
||||
server_context: {}
|
||||
)
|
||||
|
||||
refute response.is_error
|
||||
assert_equal 6, response.structured_content[:result]
|
||||
end
|
||||
|
||||
def test_multiplication
|
||||
response = CalculateTool.call(
|
||||
operation: 'multiply',
|
||||
a: 6,
|
||||
b: 7,
|
||||
server_context: {}
|
||||
)
|
||||
|
||||
refute response.is_error
|
||||
assert_equal 42, response.structured_content[:result]
|
||||
end
|
||||
|
||||
def test_division
|
||||
response = CalculateTool.call(
|
||||
operation: 'divide',
|
||||
a: 15,
|
||||
b: 3,
|
||||
server_context: {}
|
||||
)
|
||||
|
||||
refute response.is_error
|
||||
assert_equal 5.0, response.structured_content[:result]
|
||||
end
|
||||
|
||||
def test_division_by_zero
|
||||
response = CalculateTool.call(
|
||||
operation: 'divide',
|
||||
a: 10,
|
||||
b: 0,
|
||||
server_context: {}
|
||||
)
|
||||
|
||||
assert response.is_error
|
||||
assert_match(/Division by zero/, response.content.first[:text])
|
||||
end
|
||||
|
||||
def test_unknown_operation
|
||||
response = CalculateTool.call(
|
||||
operation: 'modulo',
|
||||
a: 10,
|
||||
b: 3,
|
||||
server_context: {}
|
||||
)
|
||||
|
||||
assert response.is_error
|
||||
assert_match(/Unknown operation/, response.content.first[:text])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## README.md Template
|
||||
|
||||
```markdown
|
||||
# My MCP Server
|
||||
|
||||
A Model Context Protocol server built with Ruby and the official MCP Ruby SDK.
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Tools: greet, calculate
|
||||
- ✅ Prompts: code_review
|
||||
- ✅ Resources: example-data
|
||||
- ✅ Input/output schemas
|
||||
- ✅ Tool annotations
|
||||
- ✅ Structured content
|
||||
- ✅ Full test coverage
|
||||
|
||||
## Requirements
|
||||
|
||||
- Ruby 3.0 or later
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
bundle install
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Stdio Transport
|
||||
|
||||
Run the server:
|
||||
|
||||
```bash
|
||||
bundle exec bin/mcp-server
|
||||
```
|
||||
|
||||
Then send JSON-RPC requests:
|
||||
|
||||
```bash
|
||||
{"jsonrpc":"2.0","id":"1","method":"ping"}
|
||||
{"jsonrpc":"2.0","id":"2","method":"tools/list"}
|
||||
{"jsonrpc":"2.0","id":"3","method":"tools/call","params":{"name":"greet","arguments":{"name":"Ruby"}}}
|
||||
```
|
||||
|
||||
### Rails Integration
|
||||
|
||||
Add to your Rails controller:
|
||||
|
||||
```ruby
|
||||
class McpController < ApplicationController
|
||||
def index
|
||||
server = MyMcpServer::Server.new(
|
||||
server_context: { user_id: current_user.id }
|
||||
)
|
||||
render json: server.handle_json(request.body.read)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Run tests:
|
||||
|
||||
```bash
|
||||
bundle exec rake test
|
||||
```
|
||||
|
||||
Run linter:
|
||||
|
||||
```bash
|
||||
bundle exec rake rubocop
|
||||
```
|
||||
|
||||
Run all checks:
|
||||
|
||||
```bash
|
||||
bundle exec rake
|
||||
```
|
||||
|
||||
## Integration with Claude Desktop
|
||||
|
||||
Add to `claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"my-mcp-server": {
|
||||
"command": "bundle",
|
||||
"args": ["exec", "bin/mcp-server"],
|
||||
"cwd": "/path/to/my-mcp-server"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
my-mcp-server/
|
||||
├── Gemfile # Dependencies
|
||||
├── Rakefile # Build tasks
|
||||
├── lib/ # Source code
|
||||
│ ├── my_mcp_server.rb # Main entry point
|
||||
│ └── my_mcp_server/ # Module namespace
|
||||
│ ├── server.rb # Server setup
|
||||
│ ├── tools/ # Tool implementations
|
||||
│ ├── prompts/ # Prompt templates
|
||||
│ └── resources/ # Resource handlers
|
||||
├── bin/ # Executables
|
||||
│ └── mcp-server # Stdio server
|
||||
├── test/ # Test suite
|
||||
│ ├── test_helper.rb # Test configuration
|
||||
│ └── tools/ # Tool tests
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
```
|
||||
|
||||
## Generation Instructions
|
||||
|
||||
1. **Ask for project name and description**
|
||||
2. **Generate all files** with proper naming and module structure
|
||||
3. **Use classes for tools and prompts** for better organization
|
||||
4. **Include input/output schemas** for type safety
|
||||
5. **Add tool annotations** for behavior hints
|
||||
6. **Include structured content** in responses
|
||||
7. **Implement comprehensive tests** for all tools
|
||||
8. **Follow Ruby conventions** (snake_case, modules, frozen_string_literal)
|
||||
9. **Add proper error handling** with is_error flag
|
||||
10. **Provide both stdio and HTTP** usage examples
|
||||
Reference in New Issue
Block a user