actionmcp 0.102.0 → 0.104.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +46 -3
  3. data/app/models/action_mcp/session.rb +6 -5
  4. data/lib/action_mcp/configuration.rb +44 -8
  5. data/lib/action_mcp/server/base_session.rb +5 -1
  6. data/lib/action_mcp/test_helper/session_store_assertions.rb +0 -70
  7. data/lib/action_mcp/version.rb +1 -1
  8. data/lib/action_mcp.rb +0 -1
  9. data/lib/generators/action_mcp/identifier/templates/identifier.rb.erb +4 -4
  10. data/lib/generators/action_mcp/install/templates/mcp.yml +11 -1
  11. data/lib/generators/action_mcp/tool/templates/tool.rb.erb +2 -2
  12. metadata +1 -26
  13. data/lib/action_mcp/client/active_record_session_store.rb +0 -57
  14. data/lib/action_mcp/client/base.rb +0 -225
  15. data/lib/action_mcp/client/blueprint.rb +0 -163
  16. data/lib/action_mcp/client/catalog.rb +0 -164
  17. data/lib/action_mcp/client/collection.rb +0 -168
  18. data/lib/action_mcp/client/elicitation.rb +0 -34
  19. data/lib/action_mcp/client/json_rpc_handler.rb +0 -202
  20. data/lib/action_mcp/client/logging.rb +0 -19
  21. data/lib/action_mcp/client/messaging.rb +0 -28
  22. data/lib/action_mcp/client/prompt_book.rb +0 -117
  23. data/lib/action_mcp/client/prompts.rb +0 -47
  24. data/lib/action_mcp/client/request_timeouts.rb +0 -74
  25. data/lib/action_mcp/client/resources.rb +0 -100
  26. data/lib/action_mcp/client/roots.rb +0 -13
  27. data/lib/action_mcp/client/server.rb +0 -60
  28. data/lib/action_mcp/client/session_store.rb +0 -39
  29. data/lib/action_mcp/client/session_store_factory.rb +0 -27
  30. data/lib/action_mcp/client/streamable_client.rb +0 -264
  31. data/lib/action_mcp/client/streamable_http_transport.rb +0 -306
  32. data/lib/action_mcp/client/test_session_store.rb +0 -84
  33. data/lib/action_mcp/client/toolbox.rb +0 -199
  34. data/lib/action_mcp/client/tools.rb +0 -47
  35. data/lib/action_mcp/client/transport.rb +0 -137
  36. data/lib/action_mcp/client/volatile_session_store.rb +0 -38
  37. data/lib/action_mcp/client.rb +0 -71
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d01a1c6af1a49f9992d61bfe0e0bb6aa6ab8429dbdd21e977c27506fd21cb90e
4
- data.tar.gz: 650eac8eb9711cafffee1b5e27d45873f8a28b3a9c07d27ca91d4bada3c72436
3
+ metadata.gz: 3f008e25bbf68f4c85a29052acb37e59324d302fe5b53c209f7370e80c62e21e
4
+ data.tar.gz: 8d436c6029b33e6e46b43689ea9ab063b7e8474146263d72bbbfc888fffb4b56
5
5
  SHA512:
6
- metadata.gz: 750107898a375f73d7055cb1b346971e5f1bdfe5ca82d8839a280a67c03a396970e9e7919dac5146e456cc0a49ffbe6013bfac0ec8ded7da515bd3691147a18d
7
- data.tar.gz: a1701f0a82a4320896731bc33f7db4968524133e5b6f407ac0174fed70165a36d36079e6f6f179d149305bbbbfb4977320f63d7e36afc78bc16d2f012911dac5
6
+ metadata.gz: 2f3bf909be5df38c2ca0377ac0773d6b61bfd776f25b2148aae0d218241ee80879ab43fd49dbc6b206985cf9e96a6ffe421ff0158dec48f9d9368690342b6419
7
+ data.tar.gz: 75ad95eb4c7bd7b309e2db5ae7f47722c620e1d585aed0c30f186eaec55bfa4637b99865fe7613e01b7eb217e7e6d28555b8b8ef4695c14c21a97fbbd8e94c52
data/README.md CHANGED
@@ -46,6 +46,14 @@ In short, ActionMCP helps you build an MCP server (the component that exposes ca
46
46
 
47
47
  > **Client connections:** The client part of ActionMCP is meant to connect to remote MCP servers only. Connecting to local processes (such as via STDIO) is not supported.
48
48
 
49
+ ## Requirements
50
+
51
+ - **Ruby**: 3.4.8+ or 4.0.0+
52
+ - **Rails**: 8.1.1+
53
+ - **Database**: PostgreSQL, MySQL, or SQLite3
54
+
55
+ ActionMCP is tested against Ruby 3.4.8 and 4.0.0 with Rails 8.1.1+.
56
+
49
57
  ## Installation
50
58
 
51
59
  To start using ActionMCP, add it to your project:
@@ -179,7 +187,7 @@ For tools that perform sensitive operations (file system access, database modifi
179
187
  class FileSystemTool < ApplicationMCPTool
180
188
  tool_name "read_file"
181
189
  description "Read contents of a file"
182
-
190
+
183
191
  # Require explicit consent before execution
184
192
  requires_consent!
185
193
 
@@ -392,7 +400,7 @@ ActionMCP provides comprehensive documentation across multiple specialized guide
392
400
  - **[Installation & Configuration](README.md#installation)** - Initial setup, database migrations, and basic configuration
393
401
  - **[Authentication with Gateway](README.md#authentication-with-gateway)** - User authentication and authorization patterns
394
402
 
395
- ### Component Development
403
+ ### Component Development
396
404
  - **[📋 TOOLS.MD](TOOLS.MD)** - Complete guide to developing MCP tools
397
405
  - Generator usage and best practices
398
406
  - Property definitions, validation, and consent management
@@ -462,6 +470,12 @@ module Tron
462
470
  config.action_mcp.version = "1.2.3" # defaults to "0.0.1"
463
471
  config.action_mcp.logging_enabled = true # defaults to true
464
472
  config.action_mcp.logging_level = :info # defaults to :info, can be :debug, :info, :warn, :error, :fatal
473
+
474
+ # Server instructions - helps LLMs understand the server's purpose
475
+ config.action_mcp.server_instructions = [
476
+ "Use this server to access and control Tron system programs",
477
+ "Helpful for managing system processes and user data"
478
+ ]
465
479
  end
466
480
  end
467
481
  ```
@@ -513,6 +527,35 @@ production:
513
527
  max_queue: 500 # Maximum number of tasks that can be queued
514
528
  ```
515
529
 
530
+ ### Server Instructions
531
+
532
+ Server instructions help LLMs understand **what your server is for** and **when to use it**. They describe the server's purpose and goal, not technical details like rate limits or authentication (tools are self-documented via their own descriptions).
533
+
534
+ Instructions are returned at the top level of the MCP initialization response.
535
+
536
+ You can configure server instructions in your `config/mcp.yml` file:
537
+
538
+ ```yaml
539
+ shared:
540
+ # Describe the server's purpose - helps LLMs know when to use this server
541
+ server_instructions:
542
+ - "Use this server to manage Fizzy project tickets and workflows"
543
+ - "Helpful for tracking bugs, features, and sprint planning"
544
+
545
+ development:
546
+ # Development-specific purpose description
547
+ server_instructions:
548
+ - "Development server for testing Fizzy integration"
549
+ - "Use for prototyping ticket management workflows"
550
+
551
+ production:
552
+ # Production-specific purpose description
553
+ server_instructions:
554
+ - "Production Fizzy server for managing live project data"
555
+ ```
556
+
557
+ Instructions are sent as a single string (joined by newlines) at the top level of the initialization response, helping LLMs understand your server's purpose.
558
+
516
559
  #### SolidMCP (Database-backed, Recommended)
517
560
 
518
561
  For SolidMCP, add it to your Gemfile:
@@ -1042,7 +1085,7 @@ class MyTool < ApplicationMCPTool
1042
1085
  report_error("Clear error message for the LLM")
1043
1086
  return
1044
1087
  end
1045
-
1088
+
1046
1089
  # Normal processing
1047
1090
  render(text: "Success message")
1048
1091
  end
@@ -140,11 +140,15 @@ module ActionMCP
140
140
  end
141
141
 
142
142
  def server_capabilities_payload
143
- {
143
+ payload = {
144
144
  protocolVersion: protocol_version || ActionMCP::DEFAULT_PROTOCOL_VERSION,
145
145
  serverInfo: server_info,
146
146
  capabilities: server_capabilities
147
147
  }
148
+ # Add instructions at top level if configured
149
+ instructions = ActionMCP.configuration.instructions
150
+ payload[:instructions] = instructions if instructions
151
+ payload
148
152
  end
149
153
 
150
154
  def server_capabilities
@@ -359,10 +363,7 @@ module ActionMCP
359
363
 
360
364
  # This will keep the version and name of the server when this session was created
361
365
  def set_server_info
362
- self.server_info = {
363
- name: ActionMCP.configuration.name,
364
- version: ActionMCP.configuration.version
365
- }
366
+ self.server_info = ActionMCP.configuration.server_info
366
367
  end
367
368
 
368
369
  # This can be overridden by the application in future versions
@@ -44,12 +44,12 @@ module ActionMCP
44
44
  :max_queue,
45
45
  :polling_interval,
46
46
  :connects_to,
47
- # --- Tasks Options (MCP 2025-11-25) ---
48
- :tasks_enabled,
49
- :tasks_list_enabled,
50
- :tasks_cancel_enabled,
51
- # --- Schema Validation Options ---
52
- :validate_structured_content
47
+ # --- Tasks Options (MCP 2025-11-25) ---
48
+ :tasks_enabled,
49
+ :tasks_list_enabled,
50
+ :tasks_cancel_enabled,
51
+ # --- Schema Validation Options ---
52
+ :validate_structured_content
53
53
 
54
54
  def initialize
55
55
  @logging_enabled = false
@@ -74,6 +74,9 @@ module ActionMCP
74
74
  # Schema validation - disabled by default for backward compatibility
75
75
  @validate_structured_content = false
76
76
 
77
+ # Server instructions - empty by default
78
+ @server_instructions = []
79
+
77
80
  # Gateway - resolved lazily to account for Zeitwerk autoloading
78
81
  @gateway_class_name = nil
79
82
 
@@ -91,6 +94,30 @@ module ActionMCP
91
94
  @version || (has_rails_version ? Rails.application.version.to_s : "0.0.1")
92
95
  end
93
96
 
97
+ # Server information (name and version only)
98
+ def server_info
99
+ {
100
+ name: name,
101
+ version: version
102
+ }
103
+ end
104
+
105
+ # Instructions for LLMs about the server's purpose (joined as string for MCP payload)
106
+ def instructions
107
+ return nil if server_instructions.nil? || server_instructions.empty?
108
+
109
+ server_instructions.join("\n")
110
+ end
111
+
112
+ # Custom getter/setter to ensure array elements are strings
113
+ def server_instructions
114
+ @server_instructions
115
+ end
116
+
117
+ def server_instructions=(value)
118
+ @server_instructions = parse_instructions(value)
119
+ end
120
+
94
121
  def gateway_class
95
122
  # Resolve gateway class lazily to account for Zeitwerk autoloading
96
123
  # This allows ApplicationGateway to be loaded from app/mcp even if the
@@ -343,9 +370,14 @@ module ActionMCP
343
370
  @client_session_store_type = config["client_session_store_type"].to_sym
344
371
  end
345
372
 
346
- return unless config["server_session_store_type"]
373
+ if config["server_session_store_type"]
374
+ @server_session_store_type = config["server_session_store_type"].to_sym
375
+ end
347
376
 
348
- @server_session_store_type = config["server_session_store_type"].to_sym
377
+ # Extract server instructions
378
+ if config["server_instructions"]
379
+ @server_instructions = parse_instructions(config["server_instructions"])
380
+ end
349
381
  end
350
382
 
351
383
  def should_include_all?(type)
@@ -364,6 +396,10 @@ module ActionMCP
364
396
  false
365
397
  end
366
398
 
399
+ def parse_instructions(instructions)
400
+ Array(instructions).map(&:to_s)
401
+ end
402
+
367
403
  def ensure_mcp_components_loaded
368
404
  # Only load if we haven't loaded yet - but in development, always reload
369
405
  return if @mcp_components_loaded && !Rails.env.development?
@@ -128,11 +128,15 @@ module ActionMCP
128
128
 
129
129
  # Capability methods
130
130
  def server_capabilities_payload
131
- {
131
+ payload = {
132
132
  protocolVersion: ActionMCP::LATEST_VERSION,
133
133
  serverInfo: server_info,
134
134
  capabilities: server_capabilities
135
135
  }
136
+ # Add instructions at top level if configured
137
+ instructions = ActionMCP.configuration.instructions
138
+ payload[:instructions] = instructions if instructions
139
+ payload
136
140
  end
137
141
 
138
142
  def set_protocol_version(version)
@@ -51,64 +51,6 @@ module ActionMCP
51
51
  message || "Expected #{expected} session operations#{type_desc}, got #{actual}"
52
52
  end
53
53
 
54
- # Client session store assertions
55
- def assert_client_session_saved(session_id, message = nil)
56
- assert client_session_store.session_saved?(session_id),
57
- message || "Expected client session #{session_id} to have been saved"
58
- end
59
-
60
- def assert_client_session_not_saved(session_id, message = nil)
61
- assert_not client_session_store.session_saved?(session_id),
62
- message || "Expected client session #{session_id} not to have been saved"
63
- end
64
-
65
- def assert_client_session_loaded(session_id, message = nil)
66
- assert client_session_store.session_loaded?(session_id),
67
- message || "Expected client session #{session_id} to have been loaded"
68
- end
69
-
70
- def assert_client_session_not_loaded(session_id, message = nil)
71
- assert_not client_session_store.session_loaded?(session_id),
72
- message || "Expected client session #{session_id} not to have been loaded"
73
- end
74
-
75
- def assert_client_session_updated(session_id, message = nil)
76
- assert client_session_store.session_updated?(session_id),
77
- message || "Expected client session #{session_id} to have been updated"
78
- end
79
-
80
- def assert_client_session_not_updated(session_id, message = nil)
81
- assert_not client_session_store.session_updated?(session_id),
82
- message || "Expected client session #{session_id} not to have been updated"
83
- end
84
-
85
- def assert_client_session_deleted(session_id, message = nil)
86
- assert client_session_store.session_deleted?(session_id),
87
- message || "Expected client session #{session_id} to have been deleted"
88
- end
89
-
90
- def assert_client_session_not_deleted(session_id, message = nil)
91
- assert_not client_session_store.session_deleted?(session_id),
92
- message || "Expected client session #{session_id} not to have been deleted"
93
- end
94
-
95
- def assert_client_session_operation_count(expected, type = nil, message = nil)
96
- actual = client_session_store.operation_count(type)
97
- type_desc = type ? " of type #{type}" : ""
98
- assert_equal expected, actual,
99
- message || "Expected #{expected} client session operations#{type_desc}, got #{actual}"
100
- end
101
-
102
- def assert_client_session_data_includes(session_id, expected_data, message = nil)
103
- saved_data = client_session_store.last_saved_data(session_id)
104
- assert saved_data, "No saved data found for session #{session_id}"
105
-
106
- expected_data.each do |key, value|
107
- assert_equal value, saved_data[key],
108
- message || "Expected session #{session_id} data to include #{key}: #{value}"
109
- end
110
- end
111
-
112
54
  private
113
55
 
114
56
  def server_session_store
@@ -117,18 +59,6 @@ module ActionMCP
117
59
 
118
60
  store
119
61
  end
120
-
121
- def client_session_store
122
- # This would need to be set by the test or could use a thread-local variable
123
- # For now, we'll assume it's available as an instance variable
124
- store = @client_session_store || Thread.current[:test_client_session_store]
125
- unless store
126
- raise "Client session store not set. Set @client_session_store or Thread.current[:test_client_session_store]"
127
- end
128
- raise "Client session store is not a TestSessionStore" unless store.is_a?(ActionMCP::Client::TestSessionStore)
129
-
130
- store
131
- end
132
62
  end
133
63
  end
134
64
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative "gem_version"
4
4
  module ActionMCP
5
- VERSION = "0.102.0"
5
+ VERSION = "0.104.0"
6
6
 
7
7
  class << self
8
8
  alias version gem_version
data/lib/action_mcp.rb CHANGED
@@ -29,7 +29,6 @@ end.setup
29
29
 
30
30
  module ActionMCP
31
31
  require_relative "action_mcp/version"
32
- require_relative "action_mcp/client"
33
32
 
34
33
  # Error raised when structured content doesn't match the declared output_schema
35
34
  class StructuredContentValidationError < StandardError; end
@@ -22,14 +22,14 @@ class <%= class_name %> < ActionMCP::GatewayIdentifier
22
22
  private
23
23
 
24
24
  # Add any custom helper methods here
25
- #
25
+ #
26
26
  # Example helper methods:
27
- #
27
+ #
28
28
  # def extract_credentials_from_request
29
29
  # # Custom extraction logic
30
30
  # end
31
- #
31
+ #
32
32
  # def validate_credentials(credentials)
33
33
  # # Custom validation logic
34
34
  # end
35
- end
35
+ end
@@ -16,6 +16,11 @@ shared:
16
16
  # Server-specific session store type (falls back to session_store_type if not specified)
17
17
  # server_session_store_type: active_record
18
18
 
19
+ # Server instructions - helps LLMs understand the server's purpose
20
+ # Describe what the server is for, not technical details (tools are self-documented)
21
+ # server_instructions:
22
+ # - "Use this server to manage project tickets and workflows"
23
+ # - "Helpful for tracking bugs, features, and sprint planning"
19
24
 
20
25
  # MCP capability profiles
21
26
  profiles:
@@ -46,6 +51,11 @@ development:
46
51
  # Use simple pub/sub adapter for development
47
52
  adapter: simple
48
53
 
54
+ # Development-specific purpose description
55
+ # server_instructions:
56
+ # - "Development server for testing your MCP integration"
57
+ # - "Use for prototyping and experimenting with tools"
58
+
49
59
  # Session store examples for development
50
60
  # Use volatile client sessions for faster development
51
61
  # client_session_store_type: volatile
@@ -106,4 +116,4 @@ production:
106
116
  # channel_prefix: my_mcp_app_production
107
117
  # min_threads: 10 # Minimum number of threads in the pool
108
118
  # max_threads: 20 # Maximum number of threads in the pool
109
- # max_queue: 500 # Maximum number of tasks that can be queued
119
+ # max_queue: 500 # Maximum number of tasks that can be queued
@@ -34,12 +34,12 @@ class <%= class_name %> < ApplicationMCPTool
34
34
 
35
35
  # Uncomment to allow additional properties beyond those defined above:
36
36
  # additional_properties true # Allow any additional properties
37
- # additional_properties false # Explicitly disallow additional properties
37
+ # additional_properties false # Explicitly disallow additional properties
38
38
  # additional_properties({"type" => "string"}) # Allow additional properties but restrict to strings
39
39
 
40
40
  def perform
41
41
  render(text: "Processing <%= properties.map { |p| p[:name] }.join(', ') %>")
42
-
42
+
43
43
  # If additional_properties is enabled, you can access extra parameters:
44
44
  # extra_params = additional_params
45
45
  # extra_params.each do |key, value|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionmcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.102.0
4
+ version: 0.104.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -173,31 +173,6 @@ files:
173
173
  - lib/action_mcp/base_response.rb
174
174
  - lib/action_mcp/callbacks.rb
175
175
  - lib/action_mcp/capability.rb
176
- - lib/action_mcp/client.rb
177
- - lib/action_mcp/client/active_record_session_store.rb
178
- - lib/action_mcp/client/base.rb
179
- - lib/action_mcp/client/blueprint.rb
180
- - lib/action_mcp/client/catalog.rb
181
- - lib/action_mcp/client/collection.rb
182
- - lib/action_mcp/client/elicitation.rb
183
- - lib/action_mcp/client/json_rpc_handler.rb
184
- - lib/action_mcp/client/logging.rb
185
- - lib/action_mcp/client/messaging.rb
186
- - lib/action_mcp/client/prompt_book.rb
187
- - lib/action_mcp/client/prompts.rb
188
- - lib/action_mcp/client/request_timeouts.rb
189
- - lib/action_mcp/client/resources.rb
190
- - lib/action_mcp/client/roots.rb
191
- - lib/action_mcp/client/server.rb
192
- - lib/action_mcp/client/session_store.rb
193
- - lib/action_mcp/client/session_store_factory.rb
194
- - lib/action_mcp/client/streamable_client.rb
195
- - lib/action_mcp/client/streamable_http_transport.rb
196
- - lib/action_mcp/client/test_session_store.rb
197
- - lib/action_mcp/client/toolbox.rb
198
- - lib/action_mcp/client/tools.rb
199
- - lib/action_mcp/client/transport.rb
200
- - lib/action_mcp/client/volatile_session_store.rb
201
176
  - lib/action_mcp/configuration.rb
202
177
  - lib/action_mcp/console_detector.rb
203
178
  - lib/action_mcp/content.rb
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionMCP
4
- module Client
5
- # ActiveRecord-backed session store for production
6
- class ActiveRecordSessionStore
7
- include SessionStore
8
-
9
- def load_session(session_id)
10
- session = ActionMCP::Session.find_by(id: session_id)
11
- return nil unless session
12
-
13
- {
14
- id: session.id,
15
- protocol_version: session.protocol_version,
16
- client_info: session.client_info,
17
- client_capabilities: session.client_capabilities,
18
- server_info: session.server_info,
19
- server_capabilities: session.server_capabilities,
20
- created_at: session.created_at,
21
- updated_at: session.updated_at
22
- }
23
- end
24
-
25
- def save_session(session_id, session_data)
26
- session = ActionMCP::Session.find_or_initialize_by(id: session_id)
27
-
28
- # Only assign attributes that exist in the database
29
- attributes = {}
30
- attributes[:protocol_version] = session_data[:protocol_version] if session_data.key?(:protocol_version)
31
- attributes[:client_info] = session_data[:client_info] if session_data.key?(:client_info)
32
- attributes[:client_capabilities] = session_data[:client_capabilities] if session_data.key?(:client_capabilities)
33
- attributes[:server_info] = session_data[:server_info] if session_data.key?(:server_info)
34
- attributes[:server_capabilities] = session_data[:server_capabilities] if session_data.key?(:server_capabilities)
35
-
36
- # Store any extra data in a jsonb column if available
37
- # For now, we'll skip last_event_id and session_data as they don't exist in the DB
38
-
39
- session.assign_attributes(attributes)
40
- session.save!
41
- session_data
42
- end
43
-
44
- def delete_session(session_id)
45
- ActionMCP::Session.find_by(id: session_id)&.destroy
46
- end
47
-
48
- def session_exists?(session_id)
49
- ActionMCP::Session.exists?(id: session_id)
50
- end
51
-
52
- def cleanup_expired_sessions(older_than: 24.hours.ago)
53
- ActionMCP::Session.where("updated_at < ?", older_than).delete_all
54
- end
55
- end
56
- end
57
- end