active_mcp 0.10.7 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44984b9388215cac9356b583bb1ad5a51f4704a3b962a767511fa388fec54a97
4
- data.tar.gz: 1da490ee543533d22cf415c5643d7526ae4f4f6c391263ebf684f12833fecacf
3
+ metadata.gz: dfbed9e2449de9de95e9c207351c157ebb8c4bf3c3e8483ca3c2083f4222d003
4
+ data.tar.gz: a29ac4be0a08aff010bead8ad35f46047ce698da651a709754b0ae8ec50f447c
5
5
  SHA512:
6
- metadata.gz: 753e739d05030c32407d95d608e4e47341bf615f18b3d114716f2174bd75d397f2db1dfd70423e3f009de99433c33a39b39deab827900dbb70bb8a31c381f8d7
7
- data.tar.gz: f5947091cb8ecf33994e5768c9748d7a9bdee4dc9df5548279dbd368a7c094c06abcf2fa8d4f0b9816f4ab92a6b0f3df6b2e353cabb5a004e49841f61cb1ed5e
6
+ metadata.gz: 9bcf5ed73689ac7ce9e6a83b375a5cbfb02f4b20d6c4eb0a653f27675f2bae4c12b3f37e05b62379ebf3b924fb14677f418577e4fe09c0c669ea057a36fad244
7
+ data.tar.gz: 05afff2d249672089f785232d26a9c0bc0235d600bdf4a807f4dac9bda66f52437f7f625b619f3ebf649876143525e24559b8db248ed7c51b796600f6f47036d
data/README.md CHANGED
@@ -38,7 +38,6 @@ A Ruby on Rails engine for the [Model Context Protocol (MCP)](https://modelconte
38
38
  - [Creating Resource Templates](#creating-resource-templates)
39
39
  - [💬 MCP Prompts](#-mcp-prompts)
40
40
  - [Creating Prompt](#creating-prompt)
41
- - [📥 Using Context in the Schema](#-using-context-in-the-schema)
42
41
  - [💡 Best Practices](#-best-practices)
43
42
  - [1. Create Specific Tool Classes](#1-create-specific-tool-classes)
44
43
  - [2. Validate and Sanitize Inputs](#2-validate-and-sanitize-inputs)
@@ -95,13 +94,9 @@ $ rails generate active_mcp:tool create_note
95
94
 
96
95
  ```ruby
97
96
  class CreateNoteTool < ActiveMcp::Tool::Base
98
- def tool_name
99
- "create_note"
100
- end
97
+ tool_name "create_note"
101
98
 
102
- def description
103
- "Create Note"
104
- end
99
+ description "Create Note"
105
100
 
106
101
  argument :title, :string, required: true
107
102
  argument :content, :string, required: true
@@ -118,11 +113,7 @@ end
118
113
 
119
114
  ```ruby
120
115
  class MySchema < ActiveMcp::Schema::Base
121
- def tools
122
- [
123
- CreateNoteTool.new
124
- ]
125
- end
116
+ tools CreateNoteTool
126
117
  end
127
118
  ```
128
119
 
@@ -223,13 +214,9 @@ MCP tools are Ruby classes that inherit from `ActiveMcp::Tool::Base` and define
223
214
 
224
215
  ```ruby
225
216
  class SearchUsersTool < ActiveMcp::Tool::Base
226
- def tool_name
227
- "Search Users"
228
- end
217
+ tool_name "Search Users"
229
218
 
230
- def description
231
- 'Search users by criteria'
232
- end
219
+ description 'Search users by criteria'
233
220
 
234
221
  argument :email, :string, required: false, description: 'Email to search for'
235
222
  argument :name, :string, required: false, description: 'Name to search for'
@@ -278,18 +265,14 @@ Control access to tools by overriding the `visible?` class method:
278
265
 
279
266
  ```ruby
280
267
  class AdminOnlyTool < ActiveMcp::Tool::Base
281
- def tool_name
282
- "admin_only_tool"
283
- end
268
+ tool_name "admin_only_tool"
284
269
 
285
- def description
286
- "Admin-only tool"
287
- end
270
+ description "Admin-only tool"
288
271
 
289
272
  argument :command, :string, required: true, description: "Admin command"
290
273
 
291
274
  # Only allow admins to access this tool
292
- def visible?(context:)
275
+ def self.visible?(context:)
293
276
  return false unless context
294
277
  return false unless context[:auth_info][:type] == :bearer
295
278
 
@@ -350,11 +333,7 @@ Resources are Ruby classes `**Resource`:
350
333
 
351
334
  ```ruby
352
335
  class UserResource < ActiveMcp::Resource::Base
353
- class << self
354
- def mime_type
355
- "application/json"
356
- end
357
- end
336
+ mime_type "application/json"
358
337
 
359
338
  def initialize(id:)
360
339
  @user = User.find(id)
@@ -372,7 +351,7 @@ class UserResource < ActiveMcp::Resource::Base
372
351
  @user.profile
373
352
  end
374
353
 
375
- def visible?(context:)
354
+ def self.visible?(context:)
376
355
  # Your logic...
377
356
  end
378
357
 
@@ -390,10 +369,8 @@ end
390
369
 
391
370
  ```ruby
392
371
  class MySchema < ActiveMcp::Schema::Base
393
- def resources
394
- User.all.each do |user|
395
- UserResource.new(id: user.id)
396
- end
372
+ resource UserResource, items: User.all.each do |user|
373
+ { id: user.id }
397
374
  end
398
375
  end
399
376
  ```
@@ -415,11 +392,7 @@ end
415
392
 
416
393
  ```ruby
417
394
  class ImageResource < ActiveMcp::Resource::Base
418
- class << self
419
- def mime_type
420
- "image/png"
421
- end
422
- end
395
+ mime_type "image/png"
423
396
 
424
397
  def resource_name
425
398
  "profile_image"
@@ -462,32 +435,22 @@ Resource teamplates are Ruby classes `**Resource`:
462
435
 
463
436
  ```ruby
464
437
  class UserResource < ActiveMcp::Resource::Base
465
- class << self
466
- def resource_template_name
467
- "users"
468
- end
469
-
470
- def uri_template
471
- "data://localhost/users/{id}"
472
- end
473
-
474
- def mime_type
475
- "application/json"
476
- end
477
-
478
- def description
479
- "This is a test."
480
- end
481
-
482
- def visible?(context:)
483
- # Your logic...
484
- end
485
- end
438
+ resource_template_name "users"
439
+
440
+ uri_template "data://localhost/users/{id}"
441
+
442
+ mime_type "application/json"
443
+
444
+ description "This is a test."
486
445
 
487
446
  argument :id, complete: ->(value, context) do
488
447
  User.all.pluck(:id).filter { _1.match(value) }
489
448
  end
490
449
 
450
+ def self.visible?(context:)
451
+ # Your logic...
452
+ end
453
+
491
454
  def initialize(id:)
492
455
  @user = User.find(id)
493
456
  end
@@ -512,10 +475,8 @@ end
512
475
 
513
476
  ```ruby
514
477
  class MySchema < ActiveMcp::Schema::Base
515
- def resources
516
- User.all.each do |user|
517
- UserResource.new(id: user.id)
518
- end
478
+ resource UserResource, items: User.all.each do |user|
479
+ { id: user.id }
519
480
  end
520
481
  end
521
482
  ```
@@ -530,23 +491,15 @@ Resources are Ruby classes `**Prompt`:
530
491
 
531
492
  ```ruby
532
493
  class HelloPrompt < ActiveMcp::Prompt::Base
533
- argument :name, required: true, description: "User name", complete: ->(value, context) do
534
- User.all.pluck(:name).filter { _1.match(value) }
535
- end
536
-
537
- def initialize(greeting:)
538
- @greeting = greeting
539
- end
494
+ prompt_name "hello"
540
495
 
541
- def prompt_name
542
- "hello"
543
- end
496
+ description "This is a test."
544
497
 
545
- def description
546
- "This is a test."
498
+ argument :name, required: true, description: "User name", complete: ->(value, context) do
499
+ User.all.pluck(:name).filter { _1.match(value) }
547
500
  end
548
501
 
549
- def visible?(context:)
502
+ def self.visible?(context:)
550
503
  # Your logic...
551
504
  end
552
505
 
@@ -554,7 +507,7 @@ class HelloPrompt < ActiveMcp::Prompt::Base
554
507
  [
555
508
  ActiveMcp::Message::Text.new(
556
509
  role: "user",
557
- text: "#{@greeting} #{name}"
510
+ text: "Hello! #{name}"
558
511
  ),
559
512
  ActiveMcp::Message::Image.new(
560
513
  role: "assistant",
@@ -577,41 +530,7 @@ end
577
530
 
578
531
  ```ruby
579
532
  class MySchema < ActiveMcp::Schema::Base
580
- def prompts
581
- [
582
- HelloPrompt.new(greeting: "Hello!")
583
- ]
584
- end
585
- end
586
- ```
587
-
588
- ## 📥 Using Context in the Schema
589
-
590
- ```ruby
591
- class MySchema < ActiveMcp::Schema::Base
592
- def prompts
593
- user = User.find_by_token(context[:auth_info][:token])
594
-
595
- user.greetings.map do |greeting|
596
- GreetingPrompt.new(greeting: greeting)
597
- end
598
- end
599
- end
600
- ```
601
-
602
- ```ruby
603
- class GreetingPrompt < ActiveMcp::Prompt::Base
604
- def initialize(greeting:)
605
- @greeting = greeting
606
- end
607
-
608
- def prompt_name
609
- "greeting_#{@greeting.text}"
610
- end
611
-
612
- def messages
613
- # ...
614
- end
533
+ prompt HelloPrompt
615
534
  end
616
535
  ```
617
536
 
@@ -50,7 +50,7 @@ module ActiveMcp
50
50
  @format = :jsonrpc
51
51
  render "active_mcp/prompts_list", formats: :json
52
52
  when Method::PROMPTS_GET
53
- @prompt = schema.visible_prompts.find { _1.prompt_name == params[:params][:name] }
53
+ @prompt = schema.visible_prompts.find { _1.prompt_name_value == params[:params][:name] }
54
54
  @format = :jsonrpc
55
55
  render "active_mcp/prompts_get", formats: :json
56
56
  else
@@ -91,7 +91,7 @@ module ActiveMcp
91
91
  @format = :json
92
92
  render "active_mcp/prompts_list", formats: :json
93
93
  when Method::PROMPTS_GET
94
- @prompt = schema.visible_prompts&.find { _1.prompt_name == params[:params][:name] }
94
+ @prompt = schema.visible_prompts&.find { _1.prompt_name_value == params[:params][:name] }
95
95
  @format = :json
96
96
  render "active_mcp/prompts_get", formats: :json
97
97
  else
@@ -5,11 +5,7 @@ module ActiveMcp
5
5
  private
6
6
 
7
7
  def read_resource(params:, context:)
8
- uri = if params[:jsonrpc].present?
9
- params[:params][:uri]
10
- else
11
- params[:uri]
12
- end
8
+ uri = params[:params][:uri]
13
9
 
14
10
  unless uri
15
11
  return {
@@ -29,7 +25,7 @@ module ActiveMcp
29
25
  }
30
26
  end
31
27
 
32
- if resource.respond_to?(:visible?) && !resource.visible?(context:)
28
+ if resource.class.respond_to?(:visible?) && !resource.class.visible?(context:)
33
29
  return {
34
30
  isError: true,
35
31
  contents: []
@@ -42,7 +38,7 @@ module ActiveMcp
42
38
  contents: [
43
39
  {
44
40
  uri:,
45
- mimeType: resource.class.mime_type,
41
+ mimeType: resource.class.mime_type_value,
46
42
  text: content
47
43
  }
48
44
  ]
@@ -52,7 +48,7 @@ module ActiveMcp
52
48
  contents: [
53
49
  {
54
50
  uri:,
55
- mimeType: resource.class.mime_type,
51
+ mimeType: resource.class.mime_type_value,
56
52
  blob: Base64.strict_encode64(content)
57
53
  }
58
54
  ]
@@ -5,13 +5,8 @@ module ActiveMcp
5
5
  private
6
6
 
7
7
  def execute_tool(params:, context: {})
8
- if params[:jsonrpc].present?
9
- tool_name = params[:params][:name]
10
- tool_params = params[:params][:arguments]
11
- else
12
- tool_name = params[:name]
13
- tool_params = params[:arguments]
14
- end
8
+ tool_name = params[:params][:name]
9
+ tool_params = params[:params][:arguments]
15
10
 
16
11
  unless tool_name
17
12
  return {
@@ -25,11 +20,11 @@ module ActiveMcp
25
20
  }
26
21
  end
27
22
 
28
- tool = schema.visible_tools.find do |tc|
29
- tc.tool_name == tool_name
23
+ tool_class = schema.visible_tools&.find do |tc|
24
+ tc.tool_name_value == tool_name
30
25
  end
31
26
 
32
- unless tool
27
+ unless tool_class
33
28
  return {
34
29
  isError: true,
35
30
  content: [
@@ -41,7 +36,7 @@ module ActiveMcp
41
36
  }
42
37
  end
43
38
 
44
- unless tool.visible?(context:)
39
+ unless tool_class.visible?(context:)
45
40
  return {
46
41
  isError: true,
47
42
  content: [
@@ -69,7 +64,9 @@ module ActiveMcp
69
64
  end
70
65
  end
71
66
 
72
- validation_result = tool.validate_arguments(arguments)
67
+ tool = tool_class.new
68
+
69
+ validation_result = tool.validate(arguments)
73
70
 
74
71
  if validation_result.is_a?(Hash) && validation_result[:error]
75
72
  return {
@@ -2,6 +2,6 @@ json.jsonrpc ActiveMcp::JSON_RPC_VERSION if @format == :jsonrpc
2
2
  json.id @id if @format == :jsonrpc && @id.present?
3
3
 
4
4
  json.result do
5
- json.description @prompt.description
6
- json.messages @prompt.messages(**params[:params][:arguments].permit!.to_h.symbolize_keys).map(&:to_h)
5
+ json.description @prompt.description_value
6
+ json.messages @prompt.new.messages(**params[:params][:arguments].permit!.to_h.symbolize_keys).map(&:to_h)
7
7
  end
@@ -4,9 +4,9 @@ json.id @id if @format == :jsonrpc && @id.present?
4
4
  json.result do
5
5
  json.prompts do
6
6
  json.array!(@prompts) do |prompt|
7
- json.name prompt.prompt_name
8
- json.description prompt.description
9
- json.arguments prompt.class.arguments ? prompt.class.arguments.map { _1.except(:complete) } : []
7
+ json.name prompt.prompt_name_value
8
+ json.description prompt.description_value
9
+ json.arguments prompt.arguments ? prompt.arguments.map { _1.except(:complete) } : []
10
10
  end
11
11
  end
12
12
  end
@@ -3,11 +3,11 @@ json.id @id if @format == :jsonrpc && @id.present?
3
3
 
4
4
  json.result do
5
5
  json.resourceTemplates do
6
- json.array!(@resource_templates) do |resource|
7
- json.name resource.resource_template_name
8
- json.uriTemplate resource.uri_template
9
- json.mimeType resource.mime_type
10
- json.description resource.description
6
+ json.array!(@resource_templates) do |template|
7
+ json.name template.resource_template_name_value
8
+ json.uriTemplate template.uri_template_value
9
+ json.mimeType template.mime_type_value
10
+ json.description template.description_value
11
11
  end
12
12
  end
13
13
  end
@@ -6,7 +6,7 @@ json.result do
6
6
  json.array!(@resources) do |resource|
7
7
  json.name resource.resource_name
8
8
  json.uri resource.uri
9
- json.mimeType resource.class.mime_type
9
+ json.mimeType resource.class.mime_type_value
10
10
  json.description resource.description
11
11
  end
12
12
  end
@@ -4,9 +4,9 @@ json.id @id if @format == :jsonrpc && @id.present?
4
4
  json.result do
5
5
  json.tools do
6
6
  json.array!(@tools) do |tool|
7
- json.name tool.tool_name
8
- json.description tool.description
9
- json.inputSchema tool.class.schema
7
+ json.name tool.tool_name_value
8
+ json.description tool.description_value
9
+ json.inputSchema tool.schema
10
10
  end
11
11
  end
12
12
  end
@@ -7,7 +7,7 @@ module ActiveMcp
7
7
  value = params.dig(:argument, :value)
8
8
 
9
9
  if uri_template
10
- resource_class = refs.find { _1.uri_template == uri_template }
10
+ resource_class = refs.find { _1.uri_template_value == uri_template }
11
11
  values = resource_class.arguments[arg_name.to_sym].call(value, context)
12
12
  {values:, total: values.length}
13
13
  elsif ref_name
@@ -13,7 +13,7 @@ module ActiveMcp
13
13
  type: "resource",
14
14
  resource: {
15
15
  uri: @resource.uri,
16
- mimeType: @resource.class.mime_type,
16
+ mimeType: @resource.class.mime_type_value,
17
17
  text: @resource.content
18
18
  }
19
19
  }
@@ -2,7 +2,15 @@ module ActiveMcp
2
2
  module Prompt
3
3
  class Base
4
4
  class << self
5
- attr_reader :arguments
5
+ attr_reader :prompt_name_value, :description_value, :arguments
6
+
7
+ def prompt_name(value)
8
+ @prompt_name_value = value
9
+ end
10
+
11
+ def description(value)
12
+ @description_value = value
13
+ end
6
14
 
7
15
  def argument(name, required: false, description: "", complete: -> {})
8
16
  @arguments ||= []
@@ -19,12 +27,6 @@ module ActiveMcp
19
27
  def initialize(*args, context: {})
20
28
  end
21
29
 
22
- def prompt_name
23
- end
24
-
25
- def description
26
- end
27
-
28
30
  def visible?(context: {})
29
31
  true
30
32
  end
@@ -4,12 +4,22 @@ module ActiveMcp
4
4
  module Resource
5
5
  class Base
6
6
  class << self
7
- attr_reader :schema, :arguments
7
+ attr_reader :resource_template_name_value, :description_value, :mime_type_value, :uri_template_value, :schema, :arguments
8
8
 
9
- def description
9
+ def resource_template_name(value)
10
+ @resource_template_name_value = value
10
11
  end
11
12
 
12
- def mime_type
13
+ def uri_template(value)
14
+ @uri_template_value = value
15
+ end
16
+
17
+ def description(value)
18
+ @description_value = value
19
+ end
20
+
21
+ def mime_type(value)
22
+ @mime_type_value = value
13
23
  end
14
24
 
15
25
  def argument(name, complete:)
@@ -1,46 +1,55 @@
1
1
  module ActiveMcp
2
2
  module Schema
3
3
  class Base
4
- attr_reader :context
4
+ class << self
5
+ attr_reader :tools, :resources, :prompts
5
6
 
6
- def initialize(context: {})
7
- @context = context
8
- end
7
+ def tool(value)
8
+ @tools ||= []
9
+ @tools << value
10
+ end
9
11
 
10
- def tools
11
- []
12
- end
12
+ def resource(value, items: [])
13
+ @resources ||= []
14
+ @resources << {klass: value, items:}
15
+ end
13
16
 
14
- def resources
15
- []
17
+ def prompt(value)
18
+ @prompts ||= []
19
+ @prompts << value
20
+ end
16
21
  end
17
22
 
18
- def prompts
19
- []
23
+ def initialize(context: {})
24
+ @context = context
20
25
  end
21
26
 
22
27
  def visible_resources
23
- resources&.filter do |resource|
24
- !resource.respond_to?(:visible?) || resource.visible?(context: @context)
28
+ visibles = self.class.resources&.filter do |resource|
29
+ !resource[:klass].respond_to?(:visible?) || resource[:klass].visible?(context: @context)
25
30
  end
31
+ visibles&.map do |resource|
32
+ resource[:items].map do |item|
33
+ resource[:klass].new(**item)
34
+ end
35
+ end&.flatten || []
26
36
  end
27
37
 
28
38
  def visible_resource_templates
29
- resource_instances = resources&.filter do |resource|
30
- resource.class.respond_to?(:uri_template) && (!resource.respond_to?(:visible?) || resource.visible?(context: @context))
39
+ visibles = self.class.resources&.filter do |resource|
40
+ resource[:klass].respond_to?(:uri_template) && (!resource[:klass].respond_to?(:visible?) || resource[:klass].visible?(context: @context))
31
41
  end
32
-
33
- resource_instances.map(&:class).uniq
42
+ visibles&.map { _1[:klass] } || []
34
43
  end
35
44
 
36
45
  def visible_tools
37
- tools&.filter do |tool|
46
+ self.class.tools&.filter do |tool|
38
47
  !tool.respond_to?(:visible?) || tool.visible?(context: @context)
39
48
  end
40
49
  end
41
50
 
42
51
  def visible_prompts
43
- prompts&.filter do |resource|
52
+ self.class.prompts&.filter do |resource|
44
53
  !resource.respond_to?(:visible?) || resource.visible?(context: @context)
45
54
  end
46
55
  end
@@ -119,7 +119,7 @@ module ActiveMcp
119
119
  @server.fetch(
120
120
  params: {
121
121
  method: Method::RESOURCES_LIST,
122
- arguments: {}
122
+ params: request[:params]
123
123
  }
124
124
  )[:result]
125
125
  )
@@ -131,7 +131,7 @@ module ActiveMcp
131
131
  @server.fetch(
132
132
  params: {
133
133
  method: Method::RESOURCES_TEMPLATES_LIST,
134
- arguments: {}
134
+ params: request[:params]
135
135
  }
136
136
  )[:result]
137
137
  )
@@ -147,14 +147,14 @@ module ActiveMcp
147
147
  def handle_call_tool(request)
148
148
  name = request.dig(:params, :name)
149
149
  arguments = request.dig(:params, :arguments) || {}
150
- result = @server.fetch(params: {method: Method::TOOLS_CALL, name:, arguments:})
150
+ result = @server.fetch(params: {method: Method::TOOLS_CALL, params: {name:, arguments:}})
151
151
 
152
152
  success_response(request[:id], result[:result])
153
153
  end
154
154
 
155
155
  def handle_read_resource(request)
156
156
  uri = request.dig(:params, :uri)
157
- result = @server.fetch(params: {method: Method::RESOURCES_READ, uri:, arguments: {}})
157
+ result = @server.fetch(params: {method: Method::RESOURCES_READ, params: {uri:, arguments: {}}})
158
158
 
159
159
  success_response(request[:id], result[:result])
160
160
  end
@@ -170,9 +170,7 @@ module ActiveMcp
170
170
  end
171
171
 
172
172
  def handle_get_prompt(request)
173
- name = request.dig(:params, :name)
174
- arguments = request.dig(:params, :arguments)
175
- result = @server.fetch(params: {method: Method::PROMPTS_GET, params: {name:, arguments:}})
173
+ result = @server.fetch(params: {method: Method::PROMPTS_GET, params: request[:params]})
176
174
 
177
175
  success_response(request[:id], result[:result])
178
176
  end
@@ -38,7 +38,7 @@ module ActiveMcp
38
38
  attr_reader :name, :version, :uri, :protocol_handler, :fetcher
39
39
 
40
40
  def initialize(
41
- version: ActiveMcp::VERSION,
41
+ version: "1.0.0",
42
42
  name: "ActiveMcp",
43
43
  uri: nil,
44
44
  auth: nil
@@ -4,7 +4,15 @@ module ActiveMcp
4
4
  module Tool
5
5
  class Base
6
6
  class << self
7
- attr_reader :schema
7
+ attr_reader :tool_name_value, :description_value, :schema
8
+
9
+ def tool_name(value)
10
+ @tool_name_value = value
11
+ end
12
+
13
+ def description(value)
14
+ @description_value = value
15
+ end
8
16
 
9
17
  def argument(name, type, required: false, description: "")
10
18
  @schema ||= default_schema
@@ -21,32 +29,26 @@ module ActiveMcp
21
29
  "required" => []
22
30
  }
23
31
  end
24
- end
25
-
26
- def initialize
27
- end
28
-
29
- def tool_name
30
- end
31
32
 
32
- def description
33
- end
34
-
35
- def visible?(context: {})
36
- true
33
+ def visible?(context: {})
34
+ true
35
+ end
37
36
  end
38
37
 
39
- def call(context: {}, **args)
40
- raise NotImplementedError, "#{self.class.name}#call must be implemented"
38
+ def initialize
41
39
  end
42
40
 
43
- def validate_arguments(args)
41
+ def validate(args)
44
42
  return true unless self.class.schema
45
43
 
46
44
  JSON::Validator.validate!(self.class.schema, args)
47
45
  rescue JSON::Schema::ValidationError => e
48
46
  {error: e.message}
49
47
  end
48
+
49
+ def call(context: {}, **args)
50
+ raise NotImplementedError, "#{self.class.name}#call must be implemented"
51
+ end
50
52
  end
51
53
  end
52
54
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveMcp
2
- VERSION = "0.10.7"
2
+ VERSION = "1.0.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.7
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Moeki Kawakami