model_context_protocol_riccardo 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.cursor/rules/release-changelogs.mdc +32 -0
  3. data/.gitattributes +4 -0
  4. data/.github/workflows/ci.yml +22 -0
  5. data/.gitignore +8 -0
  6. data/.rubocop.yml +5 -0
  7. data/.ruby-version +1 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +15 -0
  10. data/Gemfile.lock +117 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +473 -0
  13. data/Rakefile +17 -0
  14. data/bin/console +15 -0
  15. data/bin/rake +31 -0
  16. data/bin/setup +8 -0
  17. data/dev.yml +31 -0
  18. data/examples/stdio_server.rb +94 -0
  19. data/lib/mcp-ruby.rb +3 -0
  20. data/lib/model_context_protocol/configuration.rb +75 -0
  21. data/lib/model_context_protocol/content.rb +33 -0
  22. data/lib/model_context_protocol/instrumentation.rb +26 -0
  23. data/lib/model_context_protocol/json_rpc.rb +11 -0
  24. data/lib/model_context_protocol/methods.rb +86 -0
  25. data/lib/model_context_protocol/prompt/argument.rb +21 -0
  26. data/lib/model_context_protocol/prompt/message.rb +19 -0
  27. data/lib/model_context_protocol/prompt/result.rb +19 -0
  28. data/lib/model_context_protocol/prompt.rb +82 -0
  29. data/lib/model_context_protocol/resource/contents.rb +45 -0
  30. data/lib/model_context_protocol/resource/embedded.rb +18 -0
  31. data/lib/model_context_protocol/resource.rb +24 -0
  32. data/lib/model_context_protocol/resource_template.rb +24 -0
  33. data/lib/model_context_protocol/server.rb +258 -0
  34. data/lib/model_context_protocol/string_utils.rb +26 -0
  35. data/lib/model_context_protocol/tool/annotations.rb +27 -0
  36. data/lib/model_context_protocol/tool/input_schema.rb +26 -0
  37. data/lib/model_context_protocol/tool/response.rb +18 -0
  38. data/lib/model_context_protocol/tool.rb +85 -0
  39. data/lib/model_context_protocol/transport.rb +33 -0
  40. data/lib/model_context_protocol/transports/stdio.rb +33 -0
  41. data/lib/model_context_protocol/version.rb +5 -0
  42. data/lib/model_context_protocol.rb +44 -0
  43. data/model_context_protocol.gemspec +32 -0
  44. metadata +116 -0
data/README.md ADDED
@@ -0,0 +1,473 @@
1
+ # Model Context Protocol
2
+
3
+ A Ruby gem for implementing Model Context Protocol servers
4
+
5
+ ## MCP Server
6
+
7
+ The `ModelContextProtocol::Server` class is the core component that handles JSON-RPC requests and responses.
8
+ It implements the Model Context Protocol specification, handling model context requests and responses.
9
+
10
+ ### Key Features
11
+ - Implements JSON-RPC 2.0 message handling
12
+ - Supports protocol initialization and capability negotiation
13
+ - Manages tool registration and invocation
14
+ - Supports prompt registration and execution
15
+ - Supports resource registration and retrieval
16
+
17
+ ### Supported Methods
18
+ - `initialize` - Initializes the protocol and returns server capabilities
19
+ - `ping` - Simple health check
20
+ - `tools/list` - Lists all registered tools and their schemas
21
+ - `tools/call` - Invokes a specific tool with provided arguments
22
+ - `prompts/list` - Lists all registered prompts and their schemas
23
+ - `prompts/get` - Retrieves a specific prompt by name
24
+ - `resources/list` - Lists all registered resources and their schemas
25
+ - `resources/read` - Retrieves a specific resource by name
26
+ - `resources/templates/list` - Lists all registered resource templates and their schemas
27
+
28
+ ### Unsupported Features ( to be implemented in future versions )
29
+
30
+ - Notifications
31
+ - Log Level
32
+ - Resource subscriptions
33
+ - Completions
34
+ - Complete StreamableHTTP implementation with streaming responses
35
+
36
+ ### Usage
37
+
38
+ #### Rails Controller
39
+
40
+ When added to a Rails controller on a route that handles POST requests, your server will be compliant with non-streaming
41
+ [StreamableHTTP](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http) transport
42
+ requests.
43
+
44
+ You can use the `Server#handle_json` method to handle requests.
45
+
46
+ ```ruby
47
+ module ModelContextProtocol
48
+ class ApplicationController < ActionController::Base
49
+
50
+ def index
51
+ server = ModelContextProtocol::Server.new(
52
+ name: "my_server",
53
+ tools: [SomeTool, AnotherTool],
54
+ prompts: [MyPrompt],
55
+ server_context: { user_id: current_user.id },
56
+ )
57
+ render(json: server.handle_json(request.body.read).to_h)
58
+ end
59
+ end
60
+ end
61
+ ```
62
+
63
+ #### Stdio Transport
64
+
65
+ If you want to build a local command-line application, you can use the stdio transport:
66
+
67
+ ```ruby
68
+ #!/usr/bin/env ruby
69
+ require "model_context_protocol"
70
+ require "model_context_protocol/transports/stdio"
71
+
72
+ # Create a simple tool
73
+ class ExampleTool < ModelContextProtocol::Tool
74
+ description "A simple example tool that echoes back its arguments"
75
+ input_schema(
76
+ properties: {
77
+ message: { type: "string" },
78
+ },
79
+ required: ["message"]
80
+ )
81
+
82
+ class << self
83
+ def call(message:, server_context:)
84
+ ModelContextProtocol::Tool::Response.new([{
85
+ type: "text",
86
+ text: "Hello from example tool! Message: #{message}",
87
+ }])
88
+ end
89
+ end
90
+ end
91
+
92
+ # Set up the server
93
+ server = ModelContextProtocol::Server.new(
94
+ name: "example_server",
95
+ tools: [ExampleTool],
96
+ )
97
+
98
+ # Create and start the transport
99
+ transport = ModelContextProtocol::Transports::StdioTransport.new(server)
100
+ transport.open
101
+ ```
102
+
103
+ You can run this script and then type in requests to the server at the command line.
104
+
105
+ ```
106
+ $ ./stdio_server.rb
107
+ {"jsonrpc":"2.0","id":"1","result":"pong"}
108
+ {"jsonrpc":"2.0","id":"2","result":["ExampleTool"]}
109
+ {"jsonrpc":"2.0","id":"3","result":["ExampleTool"]}
110
+ ```
111
+
112
+ ## Configuration
113
+
114
+ The gem can be configured using the `ModelContextProtocol.configure` block:
115
+
116
+ ```ruby
117
+ ModelContextProtocol.configure do |config|
118
+ config.exception_reporter = do |exception, server_context|
119
+ # Your exception reporting logic here
120
+ # For example with Bugsnag:
121
+ Bugsnag.notify(exception) do |report|
122
+ report.add_metadata(:model_context_protocol, server_context)
123
+ end
124
+ end
125
+
126
+ config.instrumentation_callback = do |data|
127
+ puts "Got instrumentation data #{data.inspect}"
128
+ end
129
+ end
130
+ ```
131
+
132
+ or by creating an explicit configuration and passing it into the server.
133
+ This is useful for systems where an application hosts more than one MCP server but
134
+ they might require different instrumentation callbacks.
135
+
136
+ ```ruby
137
+ configuration = ModelContextProtocol::Configuration.new
138
+ configuration.exception_reporter = do |exception, server_context|
139
+ # Your exception reporting logic here
140
+ # For example with Bugsnag:
141
+ Bugsnag.notify(exception) do |report|
142
+ report.add_metadata(:model_context_protocol, server_context)
143
+ end
144
+ end
145
+
146
+ configuration.instrumentation_callback = do |data|
147
+ puts "Got instrumentation data #{data.inspect}"
148
+ end
149
+
150
+ server = ModelContextProtocol::Server.new(
151
+ # ... all other options
152
+ configuration:,
153
+ )
154
+ ```
155
+
156
+ ### Server Context and Configuration Block Data
157
+
158
+ #### `server_context`
159
+
160
+ The `server_context` is a user-defined hash that is passed into the server instance and made available to tools, prompts, and exception/instrumentation callbacks. It can be used to provide contextual information such as authentication state, user IDs, or request-specific data.
161
+
162
+ **Type:**
163
+ ```ruby
164
+ server_context: { [String, Symbol] => Any }
165
+ ```
166
+
167
+ **Example:**
168
+ ```ruby
169
+ server = ModelContextProtocol::Server.new(
170
+ name: "my_server",
171
+ server_context: { user_id: current_user.id, request_id: request.uuid }
172
+ )
173
+ ```
174
+
175
+ This hash is then passed as the `server_context` argument to tool and prompt calls, and is included in exception and instrumentation callbacks.
176
+
177
+ #### Configuration Block Data
178
+
179
+ ##### Exception Reporter
180
+
181
+ The exception reporter receives:
182
+
183
+ - `exception`: The Ruby exception object that was raised
184
+ - `server_context`: The context hash provided to the server
185
+
186
+ **Signature:**
187
+ ```ruby
188
+ exception_reporter = ->(exception, server_context) { ... }
189
+ ```
190
+
191
+ ##### Instrumentation Callback
192
+
193
+ The instrumentation callback receives a hash with the following possible keys:
194
+
195
+ - `method`: (String) The protocol method called (e.g., "ping", "tools/list")
196
+ - `tool_name`: (String, optional) The name of the tool called
197
+ - `prompt_name`: (String, optional) The name of the prompt called
198
+ - `resource_uri`: (String, optional) The URI of the resource called
199
+ - `error`: (String, optional) Error code if a lookup failed
200
+ - `duration`: (Float) Duration of the call in seconds
201
+
202
+ **Type:**
203
+ ```ruby
204
+ instrumentation_callback = ->(data) { ... }
205
+ # where data is a Hash with keys as described above
206
+ ```
207
+
208
+ **Example:**
209
+ ```ruby
210
+ config.instrumentation_callback = ->(data) do
211
+ puts "Instrumentation: #{data.inspect}"
212
+ end
213
+ ```
214
+
215
+ ### Server Protocol Version
216
+
217
+ The server's protocol version can be overridden using the `protocol_version` class method:
218
+
219
+ ```ruby
220
+ ModelContextProtocol::Server.protocol_version = "2024-11-05"
221
+ ```
222
+
223
+ 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`:
224
+
225
+ ```ruby
226
+ ModelContextProtocol::Server.protocol_version = nil
227
+ ```
228
+
229
+ 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.
230
+
231
+ ### Exception Reporting
232
+
233
+ The exception reporter receives two arguments:
234
+
235
+ - `exception`: The Ruby exception object that was raised
236
+ - `server_context`: A hash containing contextual information about where the error occurred
237
+
238
+ The server_context hash includes:
239
+
240
+ - For tool calls: `{ tool_name: "name", arguments: { ... } }`
241
+ - For general request handling: `{ request: { ... } }`
242
+
243
+ When an exception occurs:
244
+
245
+ 1. The exception is reported via the configured reporter
246
+ 2. For tool calls, a generic error response is returned to the client: `{ error: "Internal error occurred", isError: true }`
247
+ 3. For other requests, the exception is re-raised after reporting
248
+
249
+ If no exception reporter is configured, a default no-op reporter is used that silently ignores exceptions.
250
+
251
+ ## Tools
252
+
253
+ MCP spec includes [Tools](https://modelcontextprotocol.io/docs/concepts/tools) which provide functionality to LLM apps.
254
+
255
+ This gem provides a `ModelContextProtocol::Tool` class that can be used to create tools in two ways:
256
+
257
+ 1. As a class definition:
258
+
259
+ ```ruby
260
+ class MyTool < ModelContextProtocol::Tool
261
+ description "This tool performs specific functionality..."
262
+ input_schema(
263
+ properties: {
264
+ message: { type: "string" },
265
+ },
266
+ required: ["message"]
267
+ )
268
+ annotations(
269
+ title: "My Tool",
270
+ read_only_hint: true,
271
+ destructive_hint: false,
272
+ idempotent_hint: true,
273
+ open_world_hint: false
274
+ )
275
+
276
+ def self.call(message:, server_context:)
277
+ Tool::Response.new([{ type: "text", text: "OK" }])
278
+ end
279
+ end
280
+
281
+ tool = MyTool
282
+ ```
283
+
284
+ 2. By using the `ModelContextProtocol::Tool.define` method with a block:
285
+
286
+ ```ruby
287
+ tool = ModelContextProtocol::Tool.define(
288
+ name: "my_tool",
289
+ description: "This tool performs specific functionality...",
290
+ annotations: {
291
+ title: "My Tool",
292
+ read_only_hint: true
293
+ }
294
+ ) do |args, server_context|
295
+ Tool::Response.new([{ type: "text", text: "OK" }])
296
+ end
297
+ ```
298
+
299
+ The server_context parameter is the server_context passed into the server and can be used to pass per request information,
300
+ e.g. around authentication state.
301
+
302
+ ### Tool Annotations
303
+
304
+ Tools can include annotations that provide additional metadata about their behavior. The following annotations are supported:
305
+
306
+ - `title`: A human-readable title for the tool
307
+ - `read_only_hint`: Indicates if the tool only reads data (doesn't modify state)
308
+ - `destructive_hint`: Indicates if the tool performs destructive operations
309
+ - `idempotent_hint`: Indicates if the tool's operations are idempotent
310
+ - `open_world_hint`: Indicates if the tool operates in an open world context
311
+
312
+ Annotations can be set either through the class definition using the `annotations` class method or when defining a tool using the `define` method.
313
+
314
+ ## Prompts
315
+
316
+ 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.
317
+
318
+ The `ModelContextProtocol::Prompt` class provides two ways to create prompts:
319
+
320
+ 1. As a class definition with metadata:
321
+
322
+ ```ruby
323
+ class MyPrompt < ModelContextProtocol::Prompt
324
+ prompt_name "my_prompt" # Optional - defaults to underscored class name
325
+ description "This prompt performs specific functionality..."
326
+ arguments [
327
+ Prompt::Argument.new(
328
+ name: "message",
329
+ description: "Input message",
330
+ required: true
331
+ )
332
+ ]
333
+
334
+ class << self
335
+ def template(args, server_context:)
336
+ Prompt::Result.new(
337
+ description: "Response description",
338
+ messages: [
339
+ Prompt::Message.new(
340
+ role: "user",
341
+ content: Content::Text.new("User message")
342
+ ),
343
+ Prompt::Message.new(
344
+ role: "assistant",
345
+ content: Content::Text.new(args["message"])
346
+ )
347
+ ]
348
+ )
349
+ end
350
+ end
351
+ end
352
+
353
+ prompt = MyPrompt
354
+ ```
355
+
356
+ 2. Using the `ModelContextProtocol::Prompt.define` method:
357
+
358
+ ```ruby
359
+ prompt = ModelContextProtocol::Prompt.define(
360
+ name: "my_prompt",
361
+ description: "This prompt performs specific functionality...",
362
+ arguments: [
363
+ Prompt::Argument.new(
364
+ name: "message",
365
+ description: "Input message",
366
+ required: true
367
+ )
368
+ ]
369
+ ) do |args, server_context:|
370
+ Prompt::Result.new(
371
+ description: "Response description",
372
+ messages: [
373
+ Prompt::Message.new(
374
+ role: "user",
375
+ content: Content::Text.new("User message")
376
+ ),
377
+ Prompt::Message.new(
378
+ role: "assistant",
379
+ content: Content::Text.new(args["message"])
380
+ )
381
+ ]
382
+ )
383
+ end
384
+ ```
385
+
386
+ The server_context parameter is the server_context passed into the server and can be used to pass per request information,
387
+ e.g. around authentication state or user preferences.
388
+
389
+ ### Key Components
390
+
391
+ - `Prompt::Argument` - Defines input parameters for the prompt template
392
+ - `Prompt::Message` - Represents a message in the conversation with a role and content
393
+ - `Prompt::Result` - The output of a prompt template containing description and messages
394
+ - `Content::Text` - Text content for messages
395
+
396
+ ### Usage
397
+
398
+ Register prompts with the MCP server:
399
+
400
+ ```ruby
401
+ server = ModelContextProtocol::Server.new(
402
+ name: "my_server",
403
+ prompts: [MyPrompt],
404
+ server_context: { user_id: current_user.id },
405
+ )
406
+ ```
407
+
408
+ The server will handle prompt listing and execution through the MCP protocol methods:
409
+
410
+ - `prompts/list` - Lists all registered prompts and their schemas
411
+ - `prompts/get` - Retrieves and executes a specific prompt with arguments
412
+
413
+ ### Instrumentation
414
+
415
+ The server allows registering a callback to receive information about instrumentation.
416
+ To register a handler pass a proc/lambda to as `instrumentation_callback` into the server constructor.
417
+
418
+ ```ruby
419
+ ModelContextProtocol.configure do |config|
420
+ config.instrumentation_callback = do |data|
421
+ puts "Got instrumentation data #{data.inspect}"
422
+ end
423
+ end
424
+ ```
425
+
426
+ The data contains the following keys:
427
+ `method`: the metod called, e.g. `ping`, `tools/list`, `tools/call` etc
428
+ `tool_name`: the name of the tool called
429
+ `prompt_name`: the name of the prompt called
430
+ `resource_uri`: the uri of the resource called
431
+ `error`: if looking up tools/prompts etc failed, e.g. `tool_not_found`
432
+ `duration`: the duration of the call in seconds
433
+
434
+ `tool_name`, `prompt_name` and `resource_uri` are only populated if a matching handler is registered.
435
+ This is to avoid potential issues with metric cardinality
436
+
437
+ ## Resources
438
+
439
+ MCP spec includes [Resources](https://modelcontextprotocol.io/docs/concepts/resources)
440
+
441
+ The `ModelContextProtocol::Resource` class provides a way to register resources with the server.
442
+
443
+ ```ruby
444
+ resource = ModelContextProtocol::Resource.new(
445
+ uri: "example.com/my_resource",
446
+ mime_type: "text/plain",
447
+ text: "Lorem ipsum dolor sit amet"
448
+ )
449
+
450
+ server = ModelContextProtocol::Server.new(
451
+ name: "my_server",
452
+ resources: [resource],
453
+ )
454
+ ```
455
+
456
+ The server must register a handler for the `resources/read` method to retrieve a resource dynamically.
457
+
458
+ ```ruby
459
+ server.resources_read_handler do |params|
460
+ [{
461
+ uri: params[:uri],
462
+ mimeType: "text/plain",
463
+ text: "Hello, world!",
464
+ }]
465
+ end
466
+
467
+ ```
468
+
469
+ otherwise 'resources/read' requests will be a no-op.
470
+
471
+ ## Releases
472
+
473
+ TODO
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.ruby_opts = ["-W0", "-W:deprecated"]
8
+ t.libs << "test"
9
+ t.libs << "lib"
10
+ t.test_files = FileList["test/**/*_test.rb"]
11
+ end
12
+
13
+ require "rubocop/rake_task"
14
+
15
+ RuboCop::RakeTask.new
16
+
17
+ task default: [:test, :rubocop]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "model_context_protocol"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/rake ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rake' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path(
13
+ "../../Gemfile",
14
+ Pathname.new(__FILE__).realpath,
15
+ )
16
+
17
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
18
+
19
+ if File.file?(bundle_binstub)
20
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
+ load(bundle_binstub)
22
+ else
23
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
24
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
25
+ end
26
+ end
27
+
28
+ require "rubygems"
29
+ require "bundler/setup"
30
+
31
+ load Gem.bin_path("rake", "rake")
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/dev.yml ADDED
@@ -0,0 +1,31 @@
1
+ name: mcp-ruby
2
+
3
+ type: ruby
4
+
5
+ up:
6
+ - ruby
7
+ - bundler
8
+
9
+ commands:
10
+ console:
11
+ desc: Open console with the gem loaded
12
+ run: bin/console
13
+ build:
14
+ desc: Build the gem using rake build
15
+ run: bin/rake build
16
+ test:
17
+ desc: Run tests
18
+ syntax:
19
+ argument: file
20
+ optional: args...
21
+ run: |
22
+ if [[ $# -eq 0 ]]; then
23
+ bin/rake test
24
+ else
25
+ bin/rake -I test "$@"
26
+ fi
27
+ style:
28
+ desc: Run rubocop
29
+ aliases: [rubocop, lint]
30
+ run: bin/rubocop
31
+
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
+ require "model_context_protocol"
6
+ require "model_context_protocol/transports/stdio"
7
+
8
+ # Create a simple tool
9
+ class ExampleTool < MCP::Tool
10
+ description "A simple example tool that adds two numbers"
11
+ input_schema(
12
+ properties: {
13
+ a: { type: "number" },
14
+ b: { type: "number" },
15
+ },
16
+ required: ["a", "b"],
17
+ )
18
+
19
+ class << self
20
+ def call(a:, b:)
21
+ MCP::Tool::Response.new([{
22
+ type: "text",
23
+ text: "The sum of #{a} and #{b} is #{a + b}",
24
+ }])
25
+ end
26
+ end
27
+ end
28
+
29
+ # Create a simple prompt
30
+ class ExamplePrompt < MCP::Prompt
31
+ description "A simple example prompt that echoes back its arguments"
32
+ arguments [
33
+ MCP::Prompt::Argument.new(
34
+ name: "message",
35
+ description: "The message to echo back",
36
+ required: true,
37
+ ),
38
+ ]
39
+
40
+ class << self
41
+ def template(args, server_context:)
42
+ MCP::Prompt::Result.new(
43
+ messages: [
44
+ MCP::Prompt::Message.new(
45
+ role: "user",
46
+ content: MCP::Content::Text.new(args[:message]),
47
+ ),
48
+ ],
49
+ )
50
+ end
51
+ end
52
+ end
53
+
54
+ # Set up the server
55
+ server = MCP::Server.new(
56
+ name: "example_server",
57
+ tools: [ExampleTool],
58
+ prompts: [ExamplePrompt],
59
+ resources: [
60
+ MCP::Resource.new(
61
+ uri: "test_resource",
62
+ name: "Test resource",
63
+ description: "Test resource that echoes back the uri as its content",
64
+ mime_type: "text/plain",
65
+ ),
66
+ ],
67
+ )
68
+
69
+ server.define_tool(
70
+ name: "echo",
71
+ description: "A simple example tool that echoes back its arguments",
72
+ input_schema: { properties: { message: { type: "string" } }, required: ["message"] },
73
+ ) do |message:|
74
+ MCP::Tool::Response.new(
75
+ [
76
+ {
77
+ type: "text",
78
+ text: "Hello from echo tool! Message: #{message}",
79
+ },
80
+ ],
81
+ )
82
+ end
83
+
84
+ server.resources_read_handler do |params|
85
+ [{
86
+ uri: params[:uri],
87
+ mimeType: "text/plain",
88
+ text: "Hello, world!",
89
+ }]
90
+ end
91
+
92
+ # Create and start the transport
93
+ transport = MCP::Transports::StdioTransport.new(server)
94
+ transport.open
data/lib/mcp-ruby.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "model_context_protocol"