solid_mcp 0.2.3 → 0.5.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 +4 -4
- data/ext/solid_mcp_native/Cargo.toml +12 -0
- data/ext/solid_mcp_native/core/Cargo.toml +32 -0
- data/ext/solid_mcp_native/core/src/config.rs +133 -0
- data/ext/solid_mcp_native/core/src/db/mod.rs +154 -0
- data/ext/solid_mcp_native/core/src/db/postgres.rs +242 -0
- data/ext/solid_mcp_native/core/src/db/sqlite.rs +276 -0
- data/ext/solid_mcp_native/core/src/error.rs +38 -0
- data/ext/solid_mcp_native/core/src/lib.rs +25 -0
- data/ext/solid_mcp_native/core/src/message.rs +191 -0
- data/ext/solid_mcp_native/core/src/pubsub.rs +309 -0
- data/ext/solid_mcp_native/core/src/subscriber.rs +298 -0
- data/ext/solid_mcp_native/core/src/writer.rs +252 -0
- data/ext/solid_mcp_native/extconf.rb +3 -0
- data/ext/solid_mcp_native/ffi/Cargo.toml +20 -0
- data/ext/solid_mcp_native/ffi/extconf.rb +67 -0
- data/ext/solid_mcp_native/ffi/src/lib.rs +224 -0
- data/lib/solid_mcp/configuration.rb +5 -2
- data/lib/solid_mcp/message_writer.rb +80 -45
- data/lib/solid_mcp/native_speedup.rb +140 -0
- data/lib/solid_mcp/pub_sub.rb +10 -8
- data/lib/solid_mcp/subscriber.rb +18 -7
- data/lib/solid_mcp/version.rb +1 -1
- data/lib/solid_mcp.rb +3 -0
- metadata +57 -19
- data/.release-please-manifest.json +0 -1
- data/CHANGELOG.md +0 -34
- data/Gemfile +0 -11
- data/Gemfile.lock +0 -140
- data/Rakefile +0 -8
- data/app/models/solid_mcp/message.rb +0 -25
- data/app/models/solid_mcp/record.rb +0 -10
- data/bin/console +0 -11
- data/bin/rails +0 -15
- data/bin/setup +0 -8
- data/bin/test +0 -8
- data/db/migrate/20250624000001_create_solid_mcp_messages.rb +0 -28
- data/release-please-config.json +0 -8
- data/solid_mcp.gemspec +0 -39
|
@@ -6,15 +6,17 @@ require "concurrent"
|
|
|
6
6
|
module SolidMCP
|
|
7
7
|
class MessageWriter
|
|
8
8
|
include Singleton
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
# Reset the singleton (for testing only)
|
|
11
11
|
def self.reset!
|
|
12
12
|
@singleton__instance__ = nil
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def initialize
|
|
16
|
-
@queue =
|
|
16
|
+
@queue = SizedQueue.new(SolidMCP.configuration.max_queue_size)
|
|
17
17
|
@shutdown = Concurrent::AtomicBoolean.new(false)
|
|
18
|
+
@dropped_count = Concurrent::AtomicFixnum.new(0)
|
|
19
|
+
@worker_ready = Concurrent::CountDownLatch.new(1)
|
|
18
20
|
@executor = Concurrent::ThreadPoolExecutor.new(
|
|
19
21
|
min_threads: 1,
|
|
20
22
|
max_threads: 1, # Single thread for ordered writes
|
|
@@ -22,36 +24,68 @@ module SolidMCP
|
|
|
22
24
|
fallback_policy: :caller_runs
|
|
23
25
|
)
|
|
24
26
|
start_worker
|
|
27
|
+
# Wait for worker thread to be ready (with short timeout)
|
|
28
|
+
# Using 0.1s is enough for worker to start, avoids 1s delay per test
|
|
29
|
+
@worker_ready.wait(0.1)
|
|
25
30
|
end
|
|
26
31
|
|
|
27
|
-
# Called by publish API - non-blocking
|
|
32
|
+
# Called by publish API - non-blocking with backpressure
|
|
28
33
|
def enqueue(session_id, event_type, data)
|
|
29
|
-
|
|
34
|
+
message = {
|
|
30
35
|
session_id: session_id,
|
|
31
36
|
event_type: event_type,
|
|
32
37
|
data: data.is_a?(String) ? data : data.to_json,
|
|
33
|
-
created_at: Time.
|
|
38
|
+
created_at: Time.now.utc
|
|
34
39
|
}
|
|
40
|
+
|
|
41
|
+
# Try non-blocking push with backpressure
|
|
42
|
+
begin
|
|
43
|
+
@queue.push(message, true) # non-blocking
|
|
44
|
+
true
|
|
45
|
+
rescue ThreadError
|
|
46
|
+
# Queue full - drop message and log
|
|
47
|
+
@dropped_count.increment
|
|
48
|
+
SolidMCP::Logger.warn "SolidMCP queue full (#{SolidMCP.configuration.max_queue_size}), dropped message for session #{session_id}"
|
|
49
|
+
false
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get count of dropped messages
|
|
54
|
+
def dropped_count
|
|
55
|
+
@dropped_count.value
|
|
35
56
|
end
|
|
36
57
|
|
|
37
58
|
# Blocks until executor has flushed everything
|
|
38
59
|
def shutdown
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
60
|
+
SolidMCP::Logger.info "SolidMCP::MessageWriter shutting down, #{@queue.size} messages pending"
|
|
61
|
+
|
|
62
|
+
# Mark as shutting down (worker will exit after draining queue)
|
|
42
63
|
@shutdown.make_true
|
|
64
|
+
|
|
65
|
+
# Wait for executor to finish processing
|
|
43
66
|
@executor.shutdown
|
|
44
|
-
@executor.wait_for_termination(
|
|
67
|
+
@executor.wait_for_termination(SolidMCP.configuration.shutdown_timeout)
|
|
68
|
+
|
|
69
|
+
if @queue.size > 0
|
|
70
|
+
SolidMCP::Logger.warn "SolidMCP::MessageWriter shutdown timeout, #{@queue.size} messages not written"
|
|
71
|
+
end
|
|
45
72
|
end
|
|
46
73
|
|
|
47
74
|
# Force flush any pending messages (useful for tests)
|
|
48
75
|
def flush
|
|
49
76
|
return unless @executor.running?
|
|
50
|
-
|
|
77
|
+
|
|
51
78
|
# Add a marker and wait for it to be processed
|
|
52
79
|
processed = Concurrent::CountDownLatch.new(1)
|
|
53
|
-
|
|
54
|
-
|
|
80
|
+
|
|
81
|
+
# Use blocking push for flush marker (not subject to queue limits)
|
|
82
|
+
begin
|
|
83
|
+
@queue.push({ flush_marker: processed }, false) # blocking
|
|
84
|
+
rescue ThreadError
|
|
85
|
+
# Queue is shutting down
|
|
86
|
+
return
|
|
87
|
+
end
|
|
88
|
+
|
|
55
89
|
# Wait up to 1 second for flush to complete
|
|
56
90
|
processed.wait(1)
|
|
57
91
|
end
|
|
@@ -71,6 +105,9 @@ module SolidMCP
|
|
|
71
105
|
end
|
|
72
106
|
|
|
73
107
|
def run_loop
|
|
108
|
+
# Signal that worker is ready
|
|
109
|
+
@worker_ready.count_down
|
|
110
|
+
|
|
74
111
|
loop do
|
|
75
112
|
break if @shutdown.true? && @queue.empty?
|
|
76
113
|
|
|
@@ -78,8 +115,6 @@ module SolidMCP
|
|
|
78
115
|
if batch.any?
|
|
79
116
|
SolidMCP::Logger.debug "MessageWriter processing batch of #{batch.size} messages" if ENV["DEBUG_SOLID_MCP"]
|
|
80
117
|
write_batch(batch)
|
|
81
|
-
else
|
|
82
|
-
sleep SolidMCP.configuration.flush_interval
|
|
83
118
|
end
|
|
84
119
|
end
|
|
85
120
|
rescue => e
|
|
@@ -92,25 +127,32 @@ module SolidMCP
|
|
|
92
127
|
batch_size = SolidMCP.configuration.batch_size
|
|
93
128
|
flush_markers = []
|
|
94
129
|
|
|
95
|
-
#
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
130
|
+
# Get first item - use blocking pop with timeout to avoid busy spin
|
|
131
|
+
item = nil
|
|
132
|
+
until @shutdown.true? && @queue.empty?
|
|
133
|
+
begin
|
|
134
|
+
# Blocking pop with timeout allows clean shutdown checking
|
|
135
|
+
item = @queue.pop(timeout: 0.1)
|
|
136
|
+
break if item
|
|
137
|
+
rescue ThreadError
|
|
138
|
+
# Queue closed, exit
|
|
139
|
+
break
|
|
103
140
|
end
|
|
104
|
-
rescue ThreadError
|
|
105
|
-
# Signal any flush markers we've collected
|
|
106
|
-
flush_markers.each(&:count_down)
|
|
107
|
-
return batch
|
|
108
141
|
end
|
|
109
142
|
|
|
110
|
-
|
|
143
|
+
return batch unless item
|
|
144
|
+
|
|
145
|
+
# Handle flush markers
|
|
146
|
+
if item.is_a?(Hash) && item[:flush_marker]
|
|
147
|
+
flush_markers << item[:flush_marker]
|
|
148
|
+
else
|
|
149
|
+
batch << item
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Get remaining items up to batch size (non-blocking)
|
|
111
153
|
while batch.size < batch_size
|
|
112
154
|
begin
|
|
113
|
-
item = @queue.pop(true)
|
|
155
|
+
item = @queue.pop(true) # non-blocking
|
|
114
156
|
# Handle flush markers
|
|
115
157
|
if item.is_a?(Hash) && item[:flush_marker]
|
|
116
158
|
flush_markers << item[:flush_marker]
|
|
@@ -130,24 +172,17 @@ module SolidMCP
|
|
|
130
172
|
def write_batch(batch)
|
|
131
173
|
return if batch.empty?
|
|
132
174
|
|
|
133
|
-
# Use
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
[
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
].join(",")
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
sql = <<-SQL
|
|
145
|
-
INSERT INTO solid_mcp_messages (session_id, event_type, data, created_at)
|
|
146
|
-
VALUES #{values.map { |v| "(#{v})" }.join(",")}
|
|
147
|
-
SQL
|
|
148
|
-
|
|
149
|
-
conn.execute(sql)
|
|
175
|
+
# Use ActiveRecord insert_all for safety and database portability
|
|
176
|
+
records = batch.map do |msg|
|
|
177
|
+
{
|
|
178
|
+
session_id: msg[:session_id],
|
|
179
|
+
event_type: msg[:event_type],
|
|
180
|
+
data: msg[:data],
|
|
181
|
+
created_at: msg[:created_at]
|
|
182
|
+
}
|
|
150
183
|
end
|
|
184
|
+
|
|
185
|
+
SolidMCP::Message.insert_all(records)
|
|
151
186
|
rescue => e
|
|
152
187
|
SolidMCP::Logger.error "SolidMCP::MessageWriter batch write error: #{e.message}"
|
|
153
188
|
# Could implement retry logic or dead letter queue here
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Native Rust acceleration for SolidMCP (optional)
|
|
4
|
+
#
|
|
5
|
+
# This module provides a Rust-powered pub/sub engine using Tokio for async I/O.
|
|
6
|
+
# Falls back gracefully to pure Ruby if the native extension is unavailable.
|
|
7
|
+
#
|
|
8
|
+
# Features:
|
|
9
|
+
# - 50-100x faster message throughput
|
|
10
|
+
# - PostgreSQL LISTEN/NOTIFY support (no polling)
|
|
11
|
+
# - SQLite WAL mode with efficient async polling
|
|
12
|
+
# - Compile-time thread safety guarantees
|
|
13
|
+
|
|
14
|
+
module SolidMCP
|
|
15
|
+
module NativeSpeedup
|
|
16
|
+
class << self
|
|
17
|
+
def available?
|
|
18
|
+
@available ||= load_native_extension
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def version
|
|
22
|
+
return nil unless available?
|
|
23
|
+
SolidMCPNative.version
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def load_native_extension
|
|
29
|
+
return false if ENV["DISABLE_SOLID_MCP_NATIVE"]
|
|
30
|
+
|
|
31
|
+
begin
|
|
32
|
+
require "solid_mcp_native/solid_mcp_native"
|
|
33
|
+
log_info "SolidMCP native extension loaded (v#{SolidMCPNative.version})"
|
|
34
|
+
true
|
|
35
|
+
rescue LoadError => e
|
|
36
|
+
log_debug "SolidMCP native extension not available: #{e.message}"
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def log_info(msg)
|
|
42
|
+
SolidMCP::Logger.info(msg)
|
|
43
|
+
rescue StandardError
|
|
44
|
+
# Logger not ready, silently ignore
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def log_debug(msg)
|
|
48
|
+
SolidMCP::Logger.debug(msg)
|
|
49
|
+
rescue StandardError
|
|
50
|
+
# Logger not ready, silently ignore
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Override MessageWriter with native implementation
|
|
55
|
+
module MessageWriterOverride
|
|
56
|
+
def self.prepended(base)
|
|
57
|
+
# Only prepend if native extension is available
|
|
58
|
+
return unless SolidMCP::NativeSpeedup.available?
|
|
59
|
+
|
|
60
|
+
SolidMCP::Logger.debug "Enabling native MessageWriter"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def initialize
|
|
64
|
+
if SolidMCP::NativeSpeedup.available? && !@native_initialized
|
|
65
|
+
# Initialize native engine with SQLite/PostgreSQL URL
|
|
66
|
+
db_config = SolidMCP.configuration.database_config
|
|
67
|
+
database_url = build_database_url(db_config)
|
|
68
|
+
|
|
69
|
+
SolidMCPNative.init_with_config(
|
|
70
|
+
database_url,
|
|
71
|
+
SolidMCP.configuration.batch_size,
|
|
72
|
+
(SolidMCP.configuration.polling_interval * 1000).to_i, # Convert to ms
|
|
73
|
+
SolidMCP.configuration.max_queue_size
|
|
74
|
+
)
|
|
75
|
+
@native_initialized = true
|
|
76
|
+
else
|
|
77
|
+
super
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def enqueue(session_id, event_type, data)
|
|
82
|
+
if SolidMCP::NativeSpeedup.available? && @native_initialized
|
|
83
|
+
json_data = data.is_a?(String) ? data : data.to_json
|
|
84
|
+
SolidMCPNative.broadcast(session_id.to_s, event_type.to_s, json_data)
|
|
85
|
+
else
|
|
86
|
+
super
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def flush
|
|
91
|
+
if SolidMCP::NativeSpeedup.available? && @native_initialized
|
|
92
|
+
SolidMCPNative.flush
|
|
93
|
+
else
|
|
94
|
+
super
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def shutdown
|
|
99
|
+
if SolidMCP::NativeSpeedup.available? && @native_initialized
|
|
100
|
+
SolidMCPNative.shutdown
|
|
101
|
+
@native_initialized = false
|
|
102
|
+
else
|
|
103
|
+
super
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def build_database_url(config)
|
|
110
|
+
adapter = config[:adapter] || "sqlite3"
|
|
111
|
+
|
|
112
|
+
case adapter
|
|
113
|
+
when "sqlite3"
|
|
114
|
+
# SQLite URL format
|
|
115
|
+
database = config[:database] || ":memory:"
|
|
116
|
+
"sqlite://#{database}"
|
|
117
|
+
when "postgresql", "postgres"
|
|
118
|
+
# PostgreSQL URL format
|
|
119
|
+
host = config[:host] || "localhost"
|
|
120
|
+
port = config[:port] || 5432
|
|
121
|
+
database = config[:database] || "solid_mcp"
|
|
122
|
+
username = config[:username]
|
|
123
|
+
password = config[:password]
|
|
124
|
+
|
|
125
|
+
auth = username ? "#{username}:#{password}@" : ""
|
|
126
|
+
"postgres://#{auth}#{host}:#{port}/#{database}"
|
|
127
|
+
else
|
|
128
|
+
raise SolidMCP::Error, "Unsupported database adapter: #{adapter}"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Auto-load native extension on require (if available)
|
|
136
|
+
if SolidMCP::NativeSpeedup.available?
|
|
137
|
+
# MessageWriter.prepend(SolidMCP::NativeSpeedup::MessageWriterOverride)
|
|
138
|
+
# Note: Uncommenting this line enables automatic native acceleration
|
|
139
|
+
# For now, keep it opt-in until the Rust code is battle-tested
|
|
140
|
+
end
|
data/lib/solid_mcp/pub_sub.rb
CHANGED
|
@@ -13,9 +13,10 @@ module SolidMCP
|
|
|
13
13
|
|
|
14
14
|
# Subscribe to messages for a specific session
|
|
15
15
|
def subscribe(session_id, &block)
|
|
16
|
-
|
|
17
|
-
@subscriptions
|
|
18
|
-
|
|
16
|
+
# Atomically get or create callbacks array
|
|
17
|
+
callbacks = @subscriptions.compute_if_absent(session_id) { Concurrent::Array.new }
|
|
18
|
+
callbacks << block
|
|
19
|
+
|
|
19
20
|
# Start a listener for this session if not already running
|
|
20
21
|
ensure_listener_for(session_id)
|
|
21
22
|
end
|
|
@@ -43,11 +44,12 @@ module SolidMCP
|
|
|
43
44
|
private
|
|
44
45
|
|
|
45
46
|
def ensure_listener_for(session_id)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
# Atomically create and start listener only once
|
|
48
|
+
@listeners.compute_if_absent(session_id) do
|
|
49
|
+
listener = Subscriber.new(session_id, @subscriptions[session_id])
|
|
50
|
+
listener.start
|
|
51
|
+
listener
|
|
52
|
+
end
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
def stop_listener_for(session_id)
|
data/lib/solid_mcp/subscriber.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "concurrent/atomic/atomic_boolean"
|
|
4
|
+
require "concurrent/atomic/atomic_reference"
|
|
4
5
|
require "concurrent/timer_task"
|
|
5
6
|
|
|
6
7
|
module SolidMCP
|
|
@@ -9,7 +10,7 @@ module SolidMCP
|
|
|
9
10
|
@session_id = session_id
|
|
10
11
|
@callbacks = callbacks
|
|
11
12
|
@running = Concurrent::AtomicBoolean.new(false)
|
|
12
|
-
@last_message_id = 0
|
|
13
|
+
@last_message_id = Concurrent::AtomicReference.new(0)
|
|
13
14
|
@timer_task = nil
|
|
14
15
|
@max_retries = ENV["RAILS_ENV"] == "test" ? 3 : Float::INFINITY
|
|
15
16
|
@retry_count = 0
|
|
@@ -20,14 +21,14 @@ module SolidMCP
|
|
|
20
21
|
|
|
21
22
|
@running.make_true
|
|
22
23
|
@retry_count = 0
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
@timer_task = Concurrent::TimerTask.new(
|
|
25
26
|
execution_interval: SolidMCP.configuration.polling_interval,
|
|
26
27
|
run_now: true
|
|
27
28
|
) do
|
|
28
29
|
poll_once
|
|
29
30
|
end
|
|
30
|
-
|
|
31
|
+
|
|
31
32
|
@timer_task.execute
|
|
32
33
|
end
|
|
33
34
|
|
|
@@ -41,7 +42,7 @@ module SolidMCP
|
|
|
41
42
|
|
|
42
43
|
def poll_once
|
|
43
44
|
return unless @running.true?
|
|
44
|
-
|
|
45
|
+
|
|
45
46
|
# Ensure connection in thread
|
|
46
47
|
SolidMCP::Message.connection_pool.with_connection do
|
|
47
48
|
messages = fetch_new_messages
|
|
@@ -54,7 +55,7 @@ module SolidMCP
|
|
|
54
55
|
rescue => e
|
|
55
56
|
@retry_count += 1
|
|
56
57
|
SolidMCP::Logger.error "SolidMCP::Subscriber error for session #{@session_id}: #{e.message} (retry #{@retry_count}/#{@max_retries})"
|
|
57
|
-
|
|
58
|
+
|
|
58
59
|
if @retry_count >= @max_retries && @max_retries != Float::INFINITY
|
|
59
60
|
SolidMCP::Logger.error "SolidMCP::Subscriber max retries reached for session #{@session_id}, stopping"
|
|
60
61
|
stop
|
|
@@ -65,7 +66,7 @@ module SolidMCP
|
|
|
65
66
|
SolidMCP::Message
|
|
66
67
|
.for_session(@session_id)
|
|
67
68
|
.undelivered
|
|
68
|
-
.after_id(@last_message_id)
|
|
69
|
+
.after_id(@last_message_id.get)
|
|
69
70
|
.order(:id)
|
|
70
71
|
.limit(100)
|
|
71
72
|
.to_a
|
|
@@ -73,6 +74,8 @@ module SolidMCP
|
|
|
73
74
|
|
|
74
75
|
def process_messages(messages)
|
|
75
76
|
messages.each do |message|
|
|
77
|
+
# Process all callbacks first
|
|
78
|
+
all_successful = true
|
|
76
79
|
@callbacks.each do |callback|
|
|
77
80
|
begin
|
|
78
81
|
callback.call({
|
|
@@ -81,10 +84,18 @@ module SolidMCP
|
|
|
81
84
|
id: message.id
|
|
82
85
|
})
|
|
83
86
|
rescue => e
|
|
87
|
+
all_successful = false
|
|
84
88
|
SolidMCP::Logger.error "SolidMCP callback error: #{e.message}"
|
|
85
89
|
end
|
|
86
90
|
end
|
|
87
|
-
|
|
91
|
+
|
|
92
|
+
# Only update last_message_id if all callbacks succeeded
|
|
93
|
+
if all_successful
|
|
94
|
+
@last_message_id.set(message.id)
|
|
95
|
+
else
|
|
96
|
+
# Stop processing remaining messages on first callback failure
|
|
97
|
+
break
|
|
98
|
+
end
|
|
88
99
|
end
|
|
89
100
|
end
|
|
90
101
|
|
data/lib/solid_mcp/version.rb
CHANGED
data/lib/solid_mcp.rb
CHANGED
metadata
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solid_mcp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
8
|
-
bindir:
|
|
8
|
+
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
@@ -65,6 +65,20 @@ dependencies:
|
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: '1.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rb_sys
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0.9'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0.9'
|
|
68
82
|
- !ruby/object:Gem::Dependency
|
|
69
83
|
name: minitest
|
|
70
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -107,28 +121,49 @@ dependencies:
|
|
|
107
121
|
- - "~>"
|
|
108
122
|
- !ruby/object:Gem::Version
|
|
109
123
|
version: '13.0'
|
|
110
|
-
|
|
111
|
-
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: rake-compiler
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '1.2'
|
|
131
|
+
type: :development
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - "~>"
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '1.2'
|
|
138
|
+
description: |
|
|
139
|
+
SolidMCP implements a high-performance, bidirectional Pub/Sub transport for ActionMCP.
|
|
140
|
+
Features optional Rust native extension with Tokio for async I/O, PostgreSQL LISTEN/NOTIFY
|
|
141
|
+
support, and automatic fallback to pure Ruby when native extension is unavailable.
|
|
112
142
|
email:
|
|
113
143
|
- terminale@gmail.com
|
|
114
144
|
executables: []
|
|
115
|
-
extensions:
|
|
145
|
+
extensions:
|
|
146
|
+
- ext/solid_mcp_native/extconf.rb
|
|
116
147
|
extra_rdoc_files: []
|
|
117
148
|
files:
|
|
118
|
-
- ".release-please-manifest.json"
|
|
119
|
-
- CHANGELOG.md
|
|
120
|
-
- Gemfile
|
|
121
|
-
- Gemfile.lock
|
|
122
149
|
- LICENSE.txt
|
|
123
150
|
- README.md
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
151
|
+
- ext/solid_mcp_native/Cargo.toml
|
|
152
|
+
- ext/solid_mcp_native/core/Cargo.toml
|
|
153
|
+
- ext/solid_mcp_native/core/src/config.rs
|
|
154
|
+
- ext/solid_mcp_native/core/src/db/mod.rs
|
|
155
|
+
- ext/solid_mcp_native/core/src/db/postgres.rs
|
|
156
|
+
- ext/solid_mcp_native/core/src/db/sqlite.rs
|
|
157
|
+
- ext/solid_mcp_native/core/src/error.rs
|
|
158
|
+
- ext/solid_mcp_native/core/src/lib.rs
|
|
159
|
+
- ext/solid_mcp_native/core/src/message.rs
|
|
160
|
+
- ext/solid_mcp_native/core/src/pubsub.rs
|
|
161
|
+
- ext/solid_mcp_native/core/src/subscriber.rs
|
|
162
|
+
- ext/solid_mcp_native/core/src/writer.rs
|
|
163
|
+
- ext/solid_mcp_native/extconf.rb
|
|
164
|
+
- ext/solid_mcp_native/ffi/Cargo.toml
|
|
165
|
+
- ext/solid_mcp_native/ffi/extconf.rb
|
|
166
|
+
- ext/solid_mcp_native/ffi/src/lib.rs
|
|
132
167
|
- lib/generators/solid_mcp/install/install_generator.rb
|
|
133
168
|
- lib/generators/solid_mcp/install/templates/create_solid_mcp_messages.rb.erb
|
|
134
169
|
- lib/generators/solid_mcp/install/templates/solid_mcp.rb
|
|
@@ -138,13 +173,12 @@ files:
|
|
|
138
173
|
- lib/solid_mcp/engine.rb
|
|
139
174
|
- lib/solid_mcp/logger.rb
|
|
140
175
|
- lib/solid_mcp/message_writer.rb
|
|
176
|
+
- lib/solid_mcp/native_speedup.rb
|
|
141
177
|
- lib/solid_mcp/pub_sub.rb
|
|
142
178
|
- lib/solid_mcp/subscriber.rb
|
|
143
179
|
- lib/solid_mcp/test_pub_sub.rb
|
|
144
180
|
- lib/solid_mcp/version.rb
|
|
145
|
-
- release-please-config.json
|
|
146
181
|
- sig/solid_mcp.rbs
|
|
147
|
-
- solid_mcp.gemspec
|
|
148
182
|
homepage: https://github.com/seuros/solid_mcp
|
|
149
183
|
licenses:
|
|
150
184
|
- MIT
|
|
@@ -152,6 +186,10 @@ metadata:
|
|
|
152
186
|
homepage_uri: https://github.com/seuros/solid_mcp
|
|
153
187
|
source_code_uri: https://github.com/seuros/solid_mcp
|
|
154
188
|
changelog_uri: https://github.com/seuros/solid_mcp/blob/main/CHANGELOG.md
|
|
189
|
+
bug_tracker_uri: https://github.com/seuros/solid_mcp/issues
|
|
190
|
+
rubygems_mfa_required: 'true'
|
|
191
|
+
cargo_crate_name: solid_mcp_native
|
|
192
|
+
cargo_manifest_path: ext/solid_mcp_native/ffi/Cargo.toml
|
|
155
193
|
rdoc_options: []
|
|
156
194
|
require_paths:
|
|
157
195
|
- lib
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{".":"0.2.3"}
|
data/CHANGELOG.md
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
## [Unreleased]
|
|
2
|
-
|
|
3
|
-
## [0.2.3](https://github.com/seuros/solid_mcp/compare/solid_mcp/v0.2.2...solid_mcp/v0.2.3) (2025-07-01)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
### Bug Fixes
|
|
7
|
-
|
|
8
|
-
* relax version requirement ([27b6ed6](https://github.com/seuros/solid_mcp/commit/27b6ed6e8aa0340f61517e76dba5cbe38b68b6ba))
|
|
9
|
-
|
|
10
|
-
## [0.2.2](https://github.com/seuros/solid_mcp/compare/solid_mcp/v0.2.1...solid_mcp/v0.2.2) (2025-07-01)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
### Bug Fixes
|
|
14
|
-
|
|
15
|
-
* override generator namespace to prevent zeitwerk naming issues ([beadfc5](https://github.com/seuros/solid_mcp/commit/beadfc55d73d7446ed6c05db46ef16ef4ef79886))
|
|
16
|
-
|
|
17
|
-
## [0.2.1](https://github.com/seuros/solid_mcp/compare/solid_mcp/v0.2.0...solid_mcp/v0.2.1) (2025-07-01)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
### Bug Fixes
|
|
21
|
-
|
|
22
|
-
* add issues permission to release workflow ([dd6d90d](https://github.com/seuros/solid_mcp/commit/dd6d90d5d3e87987c80215401bc422abf02da1b0))
|
|
23
|
-
* add issues permission to release workflow ([1105f92](https://github.com/seuros/solid_mcp/commit/1105f926d1266f107dad80839c1ae7bd178f8bd0))
|
|
24
|
-
|
|
25
|
-
## [0.2.0](https://github.com/seuros/solid_mcp/compare/solid_mcp-v0.1.0...solid_mcp/v0.2.0) (2025-07-01)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
### Features
|
|
29
|
-
|
|
30
|
-
* initial implementation of SolidMCP ([6123b7a](https://github.com/seuros/solid_mcp/commit/6123b7a04a7a726892f04da9e33147ce2bfcfeb5))
|
|
31
|
-
|
|
32
|
-
## [0.1.0] - 2025-05-20
|
|
33
|
-
|
|
34
|
-
- Initial release
|