mcp_lite 2.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 577f4745501390e1f5c3bda2faa1fe07f75fe6bca6588f4d513ac8fac012f5a0
4
+ data.tar.gz: 7c39bf43bb1f21aee0786dc3e1c962f0d9d7f33c4b1d6e462b3ee26e90f7950b
5
+ SHA512:
6
+ metadata.gz: 1745e12fccb017eda42788736dd9776dc8e6bdd1ffa0743f5c7e3b20feee9344a9eb970156cd159c261569eab2f1bff06ea7c2a7c1ae8eff64e8f8462a3a01c5
7
+ data.tar.gz: 2f2aaf233e0838d656ace6b62a4442fbc75b57193072b80fa4ea5eb5cb24c41aff2b93b91b9e370f3850ed2374932007ee0e496da510b88fabf10c8c343d6d29
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2024 Moeki Kawakami
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,584 @@
1
+ <div align="center">
2
+ <img src="./website/public/logo.png" alt="MCP Lite" width="200"></i>
3
+ </div>
4
+
5
+ <div align="center">
6
+
7
+ <h1>MCP Lite</h1>
8
+
9
+ [![Gem Version](https://badge.fury.io/rb/mcp_lite.svg)](https://badge.fury.io/rb/mcp_lite)
10
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
11
+
12
+ The Lightweight implementation of Model Context Protocol (MCP) in Ruby.
13
+
14
+ </div>
15
+
16
+ ### Caution
17
+
18
+ This is an early version of MCP Lite. The API may change in future releases. Please check the [CHANGELOG](CHANGELOG.md) for updates.
19
+
20
+ [Active MCP](https://rubygems.org/gems/active_mcp) is deprecated. This library is a lightweight implementation of the Model Context Protocol (MCP) in Ruby, designed to be easy to use and integrate into your applications.
21
+
22
+ ## ๐Ÿ“– Table of Contents
23
+
24
+ - [๐Ÿ“– Table of Contents](#-table-of-contents)
25
+ - [โœจ Features](#-features)
26
+ - [๐Ÿ“ฆ Installation](#-installation)
27
+ - [๐Ÿš€ Setup](#-setup)
28
+ - [๐Ÿ”Œ MCP Connection](#-mcp-connection)
29
+ - [๐Ÿงฐ Creating MCP Tools](#-creating-mcp-tools)
30
+ - [๐Ÿ“‹ Input Schema](#-input-schema)
31
+ - [๐Ÿ” Authorization \& Authentication](#-authorization--authentication)
32
+ - [Authorization for Tools](#authorization-for-tools)
33
+ - [Authentication Options](#authentication-options)
34
+ - [1. Server Configuration](#1-server-configuration)
35
+ - [2. Token Verification in Tools](#2-token-verification-in-tools)
36
+ - [๐Ÿ“ฆ MCP Resources](#-mcp-resources)
37
+ - [Creating Resources](#creating-resources)
38
+ - [Resource Types](#resource-types)
39
+ - [๐Ÿ“ฆ MCP Resource Templates](#-mcp-resource-templates)
40
+ - [Creating Resource Templates](#creating-resource-templates)
41
+ - [๐Ÿ’ฌ MCP Prompts](#-mcp-prompts)
42
+ - [Creating Prompt](#creating-prompt)
43
+ - [๐Ÿ’ก Best Practices](#-best-practices)
44
+ - [1. Create Specific Tool Classes](#1-create-specific-tool-classes)
45
+ - [2. Validate and Sanitize Inputs](#2-validate-and-sanitize-inputs)
46
+ - [3. Return Structured Responses](#3-return-structured-responses)
47
+ - [๐Ÿงช Development](#-development)
48
+ - [๐Ÿ‘ฅ Contributing](#-contributing)
49
+ - [๐Ÿ“„ License](#-license)
50
+
51
+ ## ๐Ÿ“ฆ Installation
52
+
53
+ Add this line to your application's Gemfile:
54
+
55
+ ```ruby
56
+ gem 'mcp_lite'
57
+ ```
58
+
59
+ And then execute:
60
+
61
+ ```bash
62
+ $ bundle install
63
+ ```
64
+
65
+ Or install it yourself as:
66
+
67
+ ```bash
68
+ $ gem install mcp_lite
69
+ ```
70
+
71
+ ## ๐Ÿš€ Setup
72
+
73
+ ```ruby
74
+ class CreateNoteTool < McpLite::Tool::Base
75
+ tool_name "create_note"
76
+
77
+ description "Create Note"
78
+
79
+ argument :title, :string, required: true
80
+ argument :content, :string, required: true
81
+ argument :published_at, :string, required: true,
82
+ visible: ->(context) { context[:role] == "admin" }
83
+
84
+ def call(title:, content:, context:)
85
+ note = Note.create(title: title, content: content)
86
+
87
+ [{ type: "text", text: "Created note with ID: #{note.id}" }]
88
+ end
89
+ end
90
+ ```
91
+
92
+ ```ruby
93
+ class MySchema < McpLite::Schema::Base
94
+ tools CreateNoteTool
95
+ end
96
+ ```
97
+
98
+ ```ruby
99
+ class MyMcpController < McpLite::BaseController
100
+ def index
101
+ render json: MySchema.execute(param:, context:)
102
+ end
103
+ end
104
+ ```
105
+
106
+ ```ruby
107
+ Rails.application.routes.draw do
108
+ post "/mcp", to: "my_mcp#index"
109
+
110
+ # Your other routes
111
+ end
112
+ ```
113
+
114
+ ## ๐Ÿ”Œ MCP Connection Methods
115
+
116
+ Start a dedicated MCP server that communicates with your Ruby app:
117
+
118
+ ```ruby
119
+ # script/mcp_server.rb
120
+ server = McpLite::Server.new(
121
+ name: "My App MCP Server",
122
+ uri: 'https://your-app.example.com/mcp'
123
+ )
124
+ server.start
125
+ ```
126
+
127
+ Then configure your MCP client:
128
+
129
+ ```json
130
+ {
131
+ "mcpServers": {
132
+ "my-rails-app": {
133
+ "command": "/path/to/ruby",
134
+ "args": ["/path/to/script/mcp_server.rb"]
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ ## ๐Ÿงฐ Creating MCP Tools
141
+
142
+ MCP tools are Ruby classes that inherit from `McpLite::Tool::Base` and define an interface for AI to interact with your application:
143
+
144
+ ```ruby
145
+ class SearchUsersTool < McpLite::Tool::Base
146
+ tool_name "Search Users"
147
+
148
+ description 'Search users by criteria'
149
+
150
+ argument :email, :string, required: false, description: 'Email to search for'
151
+ argument :name, :string, required: false, description: 'Name to search for'
152
+ argument :limit, :integer, required: false, description: 'Maximum number of records to return'
153
+
154
+ def call(email: nil, name: nil, limit: 10, context: {})
155
+ criteria = {}
156
+ criteria[:email] = email if email.present?
157
+ criteria[:name] = name if name.present?
158
+
159
+ users = User.where(criteria).limit(limit)
160
+
161
+ [{ type: "text", text: users.attributes.to_json }]
162
+ end
163
+ end
164
+ ```
165
+
166
+ ## ๐Ÿ“‹ Input Schema
167
+
168
+ Define arguments for your tools using the `argument` method:
169
+
170
+ ```ruby
171
+ argument :name, :string, required: true, description: 'User name'
172
+ argument :age, :integer, required: false, description: 'User age'
173
+ argument :addresses, :array, required: false, description: 'User addresses'
174
+ argument :preferences, :object, required: false, description: 'User preferences'
175
+ ```
176
+
177
+ Supported types:
178
+
179
+ | Type | Description |
180
+ | ---------- | ------------------------------- |
181
+ | `:string` | Text values |
182
+ | `:integer` | Whole numbers |
183
+ | `:number` | Decimal numbers (float/decimal) |
184
+ | `:boolean` | True/false values |
185
+ | `:array` | Lists of values |
186
+ | `:object` | Hash/dictionary structures |
187
+ | `:null` | Null values |
188
+
189
+ ## ๐Ÿ” Authorization & Authentication
190
+
191
+ ### Authorization for Tools
192
+
193
+ Control access to tools by overriding the `visible?` class method:
194
+
195
+ ```ruby
196
+ class AdminOnlyTool < McpLite::Tool::Base
197
+ tool_name "admin_only_tool"
198
+
199
+ description "Admin-only tool"
200
+
201
+ argument :command, :string, required: true, description: "Admin command"
202
+
203
+ # Only allow admins to access this tool
204
+ def self.visible?(context:)
205
+ return false unless context
206
+ return false unless context[:current_user]
207
+
208
+ # Check if the token belongs to an admin
209
+ User.find_by_token(context[:current_user][:token])&.admin?
210
+ end
211
+
212
+ def call(command:, context: {})
213
+ # Tool implementation
214
+ end
215
+ end
216
+ ```
217
+
218
+ ### Authentication Options
219
+
220
+ #### 1. Server Configuration
221
+
222
+ ```ruby
223
+ server = McpLite::Server.new(
224
+ name: "My Secure MCP Server",
225
+ uri: 'http://localhost:3000/mcp',
226
+ auth: {
227
+ type: :bearer,
228
+ token: ENV['MCP_AUTH_TOKEN']
229
+ }
230
+ )
231
+ server.start
232
+ ```
233
+
234
+ #### 2. Token Verification in Tools
235
+
236
+ ```ruby
237
+ def call(resource_id:, context: {})
238
+ # Check if authentication is provided
239
+ unless context[:current_user].present?
240
+ raise "Authentication required"
241
+ end
242
+
243
+ # Proceed with authenticated operation
244
+ # ...
245
+ end
246
+ ```
247
+
248
+ ## ๐Ÿ“ฆ MCP Resources
249
+
250
+ MCP Resources allow you to share data and files with AI assistants. Resources have a URI, MIME type, and can return either text or binary data.
251
+
252
+ ### Creating Resources
253
+
254
+ Resources are Ruby classes `**Resource`:
255
+
256
+ ```ruby
257
+ class UserResource < McpLite::Resource::Base
258
+ mime_type "application/json"
259
+
260
+ def initialize(id:)
261
+ @user = User.find(id)
262
+ end
263
+
264
+ def resource_name
265
+ @user.name
266
+ end
267
+
268
+ def uri
269
+ "data://localhost/users/#{@user.id}"
270
+ end
271
+
272
+ def description
273
+ @user.profile
274
+ end
275
+
276
+ def self.visible?(context:)
277
+ # Your logic...
278
+ end
279
+
280
+ def text
281
+ # Return JSON data
282
+ {
283
+ id: @user.id,
284
+ name: @user.name,
285
+ email: @user.email,
286
+ created_at: @user.created_at
287
+ }
288
+ end
289
+ end
290
+ ```
291
+
292
+ ```ruby
293
+ class MySchema < McpLite::Schema::Base
294
+ resource UserResource, items: User.all.each do |user|
295
+ { id: user.id }
296
+ end
297
+ end
298
+ ```
299
+
300
+ ### Resource Types
301
+
302
+ Resources can return two types of content:
303
+
304
+ 1. **Text Content** - Use the `text` method to return structured data:
305
+
306
+ ```ruby
307
+ def text
308
+ # Return strings, arrays, hashes, or any JSON-serializable object
309
+ { items: Product.all.map(&:attributes) }
310
+ end
311
+ ```
312
+
313
+ 2. **Binary Content** - Use the `blob` method to return binary files:
314
+
315
+ ```ruby
316
+ class ImageResource < McpLite::Resource::Base
317
+ mime_type "image/png"
318
+
319
+ def resource_name
320
+ "profile_image"
321
+ end
322
+
323
+ def uri
324
+ "data://localhost/image"
325
+ end
326
+
327
+ def description
328
+ "Profile image"
329
+ end
330
+
331
+ def blob
332
+ # Return binary file content
333
+ File.read(Rails.root.join("public", "profile.png"))
334
+ end
335
+ end
336
+ ```
337
+
338
+ Resources can be protected using the same authorization mechanism as tools:
339
+
340
+ ```ruby
341
+ def visible?(context: {})
342
+ return false unless context
343
+ return false unless context[:current_user]
344
+
345
+ # Check if the token belongs to an admin
346
+ User.find_by_token(context[:current_user][:token])&.admin?
347
+ end
348
+ ```
349
+
350
+ ## ๐Ÿ“ฆ MCP Resource Templates
351
+
352
+ MCP Resource Teamplates allow you to define template of resources.
353
+
354
+ ### Creating Resource Templates
355
+
356
+ Resource teamplates are Ruby classes `**Resource`:
357
+
358
+ ```ruby
359
+ class UserResource < McpLite::Resource::Base
360
+ resource_template_name "users"
361
+
362
+ uri_template "data://localhost/users/{id}"
363
+
364
+ mime_type "application/json"
365
+
366
+ description "This is a test."
367
+
368
+ argument :id, complete: ->(value, context) do
369
+ User.all.pluck(:id).filter { _1.match(value) }
370
+ end
371
+
372
+ def self.visible?(context:)
373
+ # Your logic...
374
+ end
375
+
376
+ def initialize(id:)
377
+ @user = User.find(id)
378
+ end
379
+
380
+ def resource_name
381
+ @user.name
382
+ end
383
+
384
+ def description
385
+ @user.profile
386
+ end
387
+
388
+ def uri
389
+ "data://localhost/users/#{@user.name}"
390
+ end
391
+
392
+ def text
393
+ { name: @user.name }
394
+ end
395
+ end
396
+ ```
397
+
398
+ ```ruby
399
+ class MySchema < McpLite::Schema::Base
400
+ resource UserResource, items: User.all.each do |user|
401
+ { id: user.id }
402
+ end
403
+ end
404
+ ```
405
+
406
+ ## ๐Ÿ’ฌ MCP Prompts
407
+
408
+ MCP Prompts allow you to define prompt set.
409
+
410
+ ### Creating Prompt
411
+
412
+ Resources are Ruby classes `**Prompt`:
413
+
414
+ ```ruby
415
+ class HelloPrompt < McpLite::Prompt::Base
416
+ prompt_name "hello"
417
+
418
+ description "This is a test."
419
+
420
+ argument :name, required: true, description: "User name", complete: ->(value, context) do
421
+ User.all.pluck(:name).filter { _1.match(value) }
422
+ end
423
+
424
+ def self.visible?(context:)
425
+ # Your logic...
426
+ end
427
+
428
+ def messages(name:)
429
+ [
430
+ McpLite::Message::Text.new(
431
+ role: "user",
432
+ text: "Hello! #{name}"
433
+ ),
434
+ McpLite::Message::Image.new(
435
+ role: "assistant",
436
+ data: File.read(file),
437
+ mime_type: "image/png"
438
+ ),
439
+ McpLite::Message::Audio.new(
440
+ role: "user",
441
+ data: File.read(file),
442
+ mime_type: "audio/mpeg"
443
+ ),
444
+ McpLite::Message::Resource.new(
445
+ role: "assistant",
446
+ resource: UserResource.new(name: @name)
447
+ )
448
+ ]
449
+ end
450
+ end
451
+ ```
452
+
453
+ ```ruby
454
+ class MySchema < McpLite::Schema::Base
455
+ prompt HelloPrompt
456
+ end
457
+ ```
458
+
459
+ ## ๐Ÿ’ก Best Practices
460
+
461
+ ### 1. Create Specific Tool Classes
462
+
463
+ Create dedicated tool classes for each model or operation instead of generic tools:
464
+
465
+ ```ruby
466
+ # โœ… GOOD: Specific tool for a single purpose
467
+ class SearchUsersTool < McpLite::Tool::Base
468
+ # ...specific implementation
469
+ end
470
+
471
+ # โŒ BAD: Generic tool that dynamically loads models
472
+ class GenericSearchTool < McpLite::Tool::Base
473
+ # Avoid this pattern - security and maintainability issues
474
+ end
475
+ ```
476
+
477
+ ### 2. Validate and Sanitize Inputs
478
+
479
+ Always validate and sanitize inputs in your tool implementations:
480
+
481
+ ```ruby
482
+ def call(user_id:, context: {})
483
+ # Validate input
484
+ unless user_id.is_a?(Integer) || user_id.to_s.match?(/^\d+$/)
485
+ raise "Invalid user ID format"
486
+ end
487
+
488
+ # Proceed with validated data
489
+ user = User.find_by(id: user_id)
490
+ # ...
491
+ end
492
+ ```
493
+
494
+ ### 3. Return Structured Responses
495
+
496
+ Return structured responses that are easy for AI to parse:
497
+
498
+ ```ruby
499
+ def call(query:, context: {})
500
+ results = User.search(query)
501
+
502
+ [{
503
+ type: "text",
504
+ text: {
505
+ content: results.to_json(only: [:id, :name, :email]),
506
+ metadata: {
507
+ count: results.size,
508
+ query: query
509
+ }
510
+ }.to_json
511
+ }]
512
+ end
513
+ ```
514
+
515
+ ## ๐Ÿงช Development
516
+
517
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rake` to run the tests.
518
+
519
+ ## ๐Ÿ‘ฅ Contributing
520
+
521
+ Bug reports and pull requests are welcome on GitHub at https://github.com/moekiorg/mcp_lite. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/moekiorg/mcp_lite/blob/main/CODE_OF_CONDUCT.md).
522
+
523
+ ## ๐Ÿ“„ License
524
+
525
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
526
+
527
+ # MCP Lite
528
+
529
+ Model Context Protocol (MCP) implementation in Ruby.
530
+
531
+ ## Installation
532
+
533
+ Add this line to your application's Gemfile:
534
+
535
+ ```ruby
536
+ gem 'mcp_lite'
537
+ ```
538
+
539
+ ## Usage
540
+
541
+ ### Basic Usage (Without Rails)
542
+
543
+ ```ruby
544
+ require 'mcp_lite'
545
+
546
+ class MySchema < McpLite::Schema::Base
547
+ tool SearchTool
548
+ resource UserResource, items: [
549
+ {name: "UserA"},
550
+ {name: "UserB"}
551
+ ]
552
+ prompt HelloPrompt
553
+ end
554
+
555
+ # Execute MCP request
556
+ result = MySchema.execute(
557
+ params: {
558
+ method: "tools/list",
559
+ params: {},
560
+ jsonrpc: "2.0"
561
+ },
562
+ context: {
563
+ current_user: User.find_by_token(token)
564
+ }
565
+ )
566
+ ```
567
+
568
+ ### Rails Integration
569
+
570
+ ```ruby
571
+ # app/controllers/mcp_controller.rb
572
+ class McpController < McpLite::BaseController
573
+ def index
574
+ render json: MySchema.execute(param:, context:)
575
+ end
576
+ end
577
+
578
+ # config/routes.rb
579
+ Rails.application.routes.draw do
580
+ post "/mcp", to: "mcp#index"
581
+ end
582
+ ```
583
+
584
+ // ...existing documentation...
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "rake/testtask"
2
+
3
+ Rake::TestTask.new(:test) do |t|
4
+ t.libs << "test"
5
+ t.libs << "lib"
6
+ t.test_files = FileList["test/**/*_test.rb"]
7
+ end
8
+
9
+ task default: :test
@@ -0,0 +1,21 @@
1
+ module McpLite
2
+ class Completion
3
+ def complete(params: {}, context: {}, refs: [])
4
+ ref_name = params.dig(:ref, :name)
5
+ uri_template = params.dig(:ref, :uri)
6
+ arg_name = params.dig(:argument, :name)
7
+ value = params.dig(:argument, :value)
8
+
9
+ if uri_template
10
+ resource_class = refs.find { _1.uri_template_value == uri_template }
11
+ values = resource_class.arguments[arg_name.to_sym].call(value, context)
12
+ {values:, total: values.length}
13
+ elsif ref_name
14
+ prompt = refs.find { _1.prompt_name_value == ref_name }
15
+ values = prompt.arguments.find { _1[:name] == arg_name.to_sym }[:complete].call(value, context)
16
+
17
+ {values:, total: values.length}
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module McpLite
4
+ class Configuration
5
+ attr_accessor :server_name, :server_version
6
+
7
+ def initialize
8
+ @server_name = "MCP Server"
9
+ @server_version = "1.0.0"
10
+ end
11
+ end
12
+
13
+ class << self
14
+ def configure
15
+ yield config
16
+ end
17
+
18
+ def config
19
+ @config ||= Configuration.new
20
+ end
21
+ end
22
+ end