tidewave 0.3.1 → 0.4.1
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/README.md +18 -20
- data/lib/tidewave/configuration.rb +2 -1
- data/lib/tidewave/middleware.rb +28 -12
- data/lib/tidewave/railtie.rb +25 -2
- data/lib/tidewave/streamable_http_transport.rb +131 -0
- data/lib/tidewave/tools/execute_sql_query.rb +6 -0
- data/lib/tidewave/tools/project_eval.rb +6 -0
- data/lib/tidewave/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d3375b13a00fb5b052a2bea5c6b720056379f16a26cc984ffde2973d2d2fca90
|
|
4
|
+
data.tar.gz: b5d7c9d2290e2273116712116fcb8336ddbc4009be187ca0413ec3c727b33e6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ad6d175fef2e61d40ab97de1bbf36bfaf3f37137ee7d54d6cc1f7987096e105e37381376e6f8263b70b31795065824b459225512d62e9ae5615c9dc098a8b2d
|
|
7
|
+
data.tar.gz: 2535c2570a69f70114705b2ec13f708b17bae00aefcaae504c97323c2c07edb3686a142d40f6a85f42a7619754f93fb8cbcd7267edcbb349fc621004f15c3ae2
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Tidewave
|
|
2
2
|
|
|
3
|
-
Tidewave is the coding agent for full-stack web app development,
|
|
3
|
+
Tidewave is the coding agent for full-stack web app development. Integrate Claude Code, OpenAI Codex, and other agents with your web app and web framework at every layer, from UI to database. [See our website](https://tidewave.ai) for more information.
|
|
4
4
|
|
|
5
5
|
This project can also be used as a standalone Model Context Protocol server for your editors.
|
|
6
6
|
|
|
@@ -12,12 +12,24 @@ You can install Tidewave by adding the `tidewave` gem to the development group i
|
|
|
12
12
|
gem "tidewave", group: :development
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Now
|
|
15
|
+
Now make sure [Tidewave is installed](https://hexdocs.pm/tidewave/installation.html) and you are ready to connect Tidewave to your app.
|
|
16
16
|
|
|
17
17
|
## Troubleshooting
|
|
18
18
|
|
|
19
|
+
### Content security policy
|
|
20
|
+
|
|
21
|
+
If you have enabled Content-Security-Policy, Tidewave will automatically enable "unsafe-eval" under `script-src` in order for contextual browser testing to work correctly. It also disables the `frame-ancestors` directive.
|
|
22
|
+
|
|
23
|
+
### Production Environment
|
|
24
|
+
|
|
25
|
+
Tidewave is a powerful tool that can help you develop your web application faster and more efficiently. However, it is important to note that Tidewave is not meant to be used in a production environment.
|
|
26
|
+
|
|
27
|
+
Tidewave will raise an error if it is used in any environment where code reloading is disabled (which typically includes production).
|
|
28
|
+
|
|
19
29
|
### Localhost requirement
|
|
20
30
|
|
|
31
|
+
> This requirement only matters if you are not using the Tidewave app/CLI.
|
|
32
|
+
|
|
21
33
|
Tidewave expects your web application to be running on `localhost`. If you are not running on localhost, you may need to set some additional configuration. In particular, you must configure Tidewave to allow `allow_remote_access` and [optionally configure your Rails hosts](https://guides.rubyonrails.org/configuring.html#actiondispatch-hostauthorization). For example, in your `config/environments/development.rb`:
|
|
22
34
|
|
|
23
35
|
```ruby
|
|
@@ -27,33 +39,19 @@ config.tidewave.allow_remote_access = true
|
|
|
27
39
|
|
|
28
40
|
If you want to use Docker for development, you either need to enable the configuration above or automatically redirect the relevant ports, as done by [devcontainers](https://code.visualstudio.com/docs/devcontainers/containers). See our [containers](https://hexdocs.pm/tidewave/containers.html) guide for more information.
|
|
29
41
|
|
|
30
|
-
### Content security policy
|
|
31
|
-
|
|
32
|
-
If you have enabled Content-Security-Policy, Tidewave will automatically enable "unsafe-eval" under `script-src` in order for contextual browser testing to work correctly.
|
|
33
|
-
|
|
34
|
-
### Web server requirements
|
|
35
|
-
|
|
36
|
-
At the moment, Tidewave requires all requests to be processed by the same process. In case Tidewave cannot connect to your application, consider starting your Rails application as follows:
|
|
37
|
-
|
|
38
|
-
RAILS_MAX_THREADS=1 WEB_CONCURRENCY=1 rails server
|
|
39
|
-
|
|
40
|
-
### Production Environment
|
|
41
|
-
|
|
42
|
-
Tidewave is a powerful tool that can help you develop your web application faster and more efficiently. However, it is important to note that Tidewave is not meant to be used in a production environment.
|
|
43
|
-
|
|
44
|
-
Tidewave will raise an error if it is used in any environment where code reloading is disabled (which typically includes production).
|
|
45
|
-
|
|
46
42
|
## Configuration
|
|
47
43
|
|
|
48
44
|
You may configure `tidewave` using the following syntax:
|
|
49
45
|
|
|
50
46
|
```ruby
|
|
51
|
-
config.tidewave.
|
|
47
|
+
config.tidewave.team = { id: "my-company" }
|
|
52
48
|
```
|
|
53
49
|
|
|
54
50
|
The following config is available:
|
|
55
51
|
|
|
56
|
-
* `allow_remote_access` - Tidewave only allows requests from localhost by default, even if your server listens on other interfaces
|
|
52
|
+
* `allow_remote_access` - Tidewave only allows requests from localhost by default, even if your server listens on other interfaces. If you trust your network and need to access Tidewave from a different machine, this configuration can be set to `true`
|
|
53
|
+
|
|
54
|
+
* `logger_middleware` - The logger middleware Tidewave should wrap to silence its own logs
|
|
57
55
|
|
|
58
56
|
* `preferred_orm` - which ORM to use, either `:active_record` (default) or `:sequel`
|
|
59
57
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Tidewave
|
|
4
4
|
class Configuration
|
|
5
|
-
attr_accessor :logger, :allow_remote_access, :preferred_orm, :dev, :client_url, :team
|
|
5
|
+
attr_accessor :logger, :allow_remote_access, :preferred_orm, :dev, :client_url, :team, :logger_middleware
|
|
6
6
|
|
|
7
7
|
def initialize
|
|
8
8
|
@logger = nil
|
|
@@ -11,6 +11,7 @@ module Tidewave
|
|
|
11
11
|
@dev = false
|
|
12
12
|
@client_url = "https://tidewave.ai"
|
|
13
13
|
@team = {}
|
|
14
|
+
@logger_middleware = nil
|
|
14
15
|
end
|
|
15
16
|
end
|
|
16
17
|
end
|
data/lib/tidewave/middleware.rb
CHANGED
|
@@ -8,12 +8,13 @@ require "active_support/core_ext/class"
|
|
|
8
8
|
require "active_support/core_ext/object/blank"
|
|
9
9
|
require "json"
|
|
10
10
|
require "erb"
|
|
11
|
+
require_relative "streamable_http_transport"
|
|
11
12
|
|
|
12
13
|
class Tidewave::Middleware
|
|
13
14
|
TIDEWAVE_ROUTE = "tidewave".freeze
|
|
14
|
-
|
|
15
|
-
MESSAGES_ROUTE = "mcp/message".freeze
|
|
15
|
+
MCP_ROUTE = "mcp".freeze
|
|
16
16
|
SHELL_ROUTE = "shell".freeze
|
|
17
|
+
CONFIG_ROUTE = "config".freeze
|
|
17
18
|
|
|
18
19
|
INVALID_IP = <<~TEXT.freeze
|
|
19
20
|
For security reasons, Tidewave does not accept remote connections by default.
|
|
@@ -30,9 +31,8 @@ class Tidewave::Middleware
|
|
|
30
31
|
@app = FastMcp.rack_middleware(app,
|
|
31
32
|
name: "tidewave",
|
|
32
33
|
version: Tidewave::VERSION,
|
|
33
|
-
path_prefix: "/" + TIDEWAVE_ROUTE,
|
|
34
|
-
|
|
35
|
-
sse_route: SSE_ROUTE,
|
|
34
|
+
path_prefix: "/" + TIDEWAVE_ROUTE + "/" + MCP_ROUTE,
|
|
35
|
+
transport: Tidewave::StreamableHttpTransport,
|
|
36
36
|
logger: config.logger || Logger.new(Rails.root.join("log", "tidewave.log")),
|
|
37
37
|
# Rails runs the HostAuthorization in dev, so we skip this
|
|
38
38
|
allowed_origins: [],
|
|
@@ -62,23 +62,26 @@ class Tidewave::Middleware
|
|
|
62
62
|
case [ request.request_method, path ]
|
|
63
63
|
when [ "GET", [ TIDEWAVE_ROUTE ] ]
|
|
64
64
|
return home(request)
|
|
65
|
+
when [ "GET", [ TIDEWAVE_ROUTE, CONFIG_ROUTE ] ]
|
|
66
|
+
return config_endpoint(request)
|
|
65
67
|
when [ "POST", [ TIDEWAVE_ROUTE, SHELL_ROUTE ] ]
|
|
66
68
|
return shell(request)
|
|
67
69
|
end
|
|
68
70
|
end
|
|
69
71
|
|
|
70
|
-
@app.call(env)
|
|
72
|
+
status, headers, body = @app.call(env)
|
|
73
|
+
|
|
74
|
+
# Remove X-Frame-Options headers for non-Tidewave routes to allow embedding.
|
|
75
|
+
# CSP headers are configured in the CSP application environment.
|
|
76
|
+
headers.delete("X-Frame-Options")
|
|
77
|
+
|
|
78
|
+
[ status, headers, body ]
|
|
71
79
|
end
|
|
72
80
|
|
|
73
81
|
private
|
|
74
82
|
|
|
75
83
|
def home(request)
|
|
76
|
-
config =
|
|
77
|
-
"project_name" => @project_name,
|
|
78
|
-
"framework_type" => "rails",
|
|
79
|
-
"tidewave_version" => Tidewave::VERSION,
|
|
80
|
-
"team" => @team
|
|
81
|
-
}
|
|
84
|
+
config = config_data
|
|
82
85
|
|
|
83
86
|
html = <<~HTML
|
|
84
87
|
<html>
|
|
@@ -95,6 +98,19 @@ class Tidewave::Middleware
|
|
|
95
98
|
[ 200, { "Content-Type" => "text/html" }, [ html ] ]
|
|
96
99
|
end
|
|
97
100
|
|
|
101
|
+
def config_endpoint(request)
|
|
102
|
+
[ 200, { "Content-Type" => "application/json" }, [ JSON.generate(config_data) ] ]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def config_data
|
|
106
|
+
{
|
|
107
|
+
"project_name" => @project_name,
|
|
108
|
+
"framework_type" => "rails",
|
|
109
|
+
"tidewave_version" => Tidewave::VERSION,
|
|
110
|
+
"team" => @team
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
|
|
98
114
|
def forbidden(message)
|
|
99
115
|
Rails.logger.warn(message)
|
|
100
116
|
[ 403, { "Content-Type" => "text/plain" }, [ message ] ]
|
data/lib/tidewave/railtie.rb
CHANGED
|
@@ -10,6 +10,27 @@ require "tidewave/quiet_requests_middleware"
|
|
|
10
10
|
gem_tools_path = File.expand_path("tools/**/*.rb", __dir__)
|
|
11
11
|
Dir[gem_tools_path].each { |f| require f }
|
|
12
12
|
|
|
13
|
+
# Temporary monkey patching to address regression in FastMCP
|
|
14
|
+
if Dry::Schema::Macros::Hash.method_defined?(:original_call)
|
|
15
|
+
Dry::Schema::Macros::Hash.class_eval do
|
|
16
|
+
def call(*args, &block)
|
|
17
|
+
if block
|
|
18
|
+
# Use current context to track nested context if available
|
|
19
|
+
context = MetadataContext.current
|
|
20
|
+
if context
|
|
21
|
+
context.with_nested(name) do
|
|
22
|
+
original_call(*args, &block)
|
|
23
|
+
end
|
|
24
|
+
else
|
|
25
|
+
original_call(*args, &block)
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
original_call(*args)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
13
34
|
module Tidewave
|
|
14
35
|
class Railtie < Rails::Railtie
|
|
15
36
|
config.tidewave = Tidewave::Configuration.new()
|
|
@@ -32,6 +53,8 @@ module Tidewave
|
|
|
32
53
|
content_security_policy.directives["script-src"].try do |script_src|
|
|
33
54
|
script_src << "'unsafe-eval'" unless script_src.include?("'unsafe-eval'")
|
|
34
55
|
end
|
|
56
|
+
|
|
57
|
+
content_security_policy.directives.delete("frame-ancestors")
|
|
35
58
|
end
|
|
36
59
|
end
|
|
37
60
|
end
|
|
@@ -39,7 +62,6 @@ module Tidewave
|
|
|
39
62
|
initializer "tidewave.intercept_exceptions" do |app|
|
|
40
63
|
# We intercept exceptions from DebugExceptions, format the
|
|
41
64
|
# information as text and inject into the exception page html.
|
|
42
|
-
|
|
43
65
|
ActionDispatch::DebugExceptions.register_interceptor do |request, exception|
|
|
44
66
|
request.set_header("tidewave.exception", exception)
|
|
45
67
|
end
|
|
@@ -49,7 +71,8 @@ module Tidewave
|
|
|
49
71
|
|
|
50
72
|
initializer "tidewave.logging" do |app|
|
|
51
73
|
# Do not pollute user logs with tidewave requests.
|
|
52
|
-
app.
|
|
74
|
+
logger_middleware = app.config.tidewave.logger_middleware || Rails::Rack::Logger
|
|
75
|
+
app.middleware.insert_before(logger_middleware, Tidewave::QuietRequestsMiddleware)
|
|
53
76
|
end
|
|
54
77
|
end
|
|
55
78
|
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "rack"
|
|
5
|
+
require "fast_mcp"
|
|
6
|
+
|
|
7
|
+
module Tidewave
|
|
8
|
+
# Streamable HTTP transport for MCP (POST-only, no SSE)
|
|
9
|
+
# This transport implements a simplified version of the MCP Streamable HTTP protocol
|
|
10
|
+
# that only supports POST requests for JSON-RPC messages. Unlike the full protocol,
|
|
11
|
+
# it does not support Server-Sent Events (SSE) for streaming responses.
|
|
12
|
+
class StreamableHttpTransport < FastMcp::Transports::BaseTransport
|
|
13
|
+
attr_reader :app, :path
|
|
14
|
+
|
|
15
|
+
def initialize(app, server, options = {})
|
|
16
|
+
super(server, logger: options[:logger])
|
|
17
|
+
@app = app
|
|
18
|
+
@path = options[:path_prefix] || "/mcp"
|
|
19
|
+
@running = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def start
|
|
23
|
+
@logger.debug("Starting Streamable HTTP transport (POST-only) at path: #{@path}")
|
|
24
|
+
@running = true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def stop
|
|
28
|
+
@logger.debug("Stopping Streamable HTTP transport")
|
|
29
|
+
@running = false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Send a message - capture response for synchronous HTTP
|
|
33
|
+
# Required by FastMCP::Transports::BaseTransport interface
|
|
34
|
+
def send_message(message)
|
|
35
|
+
@logger.debug("send_message called, capturing response: #{message.inspect}")
|
|
36
|
+
@captured_response = message
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def call(env)
|
|
40
|
+
request = Rack::Request.new(env)
|
|
41
|
+
|
|
42
|
+
if request.path == @path
|
|
43
|
+
@server.transport = self
|
|
44
|
+
handle_mcp_request(request, env)
|
|
45
|
+
else
|
|
46
|
+
@app.call(env)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def handle_mcp_request(request, env)
|
|
53
|
+
if request.post?
|
|
54
|
+
handle_post_request(request)
|
|
55
|
+
else
|
|
56
|
+
method_not_allowed_response
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def handle_post_request(request)
|
|
61
|
+
@logger.debug("Received POST request to MCP endpoint")
|
|
62
|
+
|
|
63
|
+
begin
|
|
64
|
+
body = request.body.read
|
|
65
|
+
message = JSON.parse(body)
|
|
66
|
+
|
|
67
|
+
@logger.debug("Processing message: #{message.inspect}")
|
|
68
|
+
|
|
69
|
+
unless valid_jsonrpc_message?(message)
|
|
70
|
+
return json_rpc_error_response(400, -32600, "Invalid Request", nil)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Capture the response that will be sent via send_message
|
|
74
|
+
@captured_response = nil
|
|
75
|
+
@server.handle_json_request(message)
|
|
76
|
+
|
|
77
|
+
@logger.debug("Sending response: #{@captured_response.inspect}")
|
|
78
|
+
|
|
79
|
+
[
|
|
80
|
+
200,
|
|
81
|
+
{ "Content-Type" => "application/json" },
|
|
82
|
+
[ JSON.generate(@captured_response) ]
|
|
83
|
+
]
|
|
84
|
+
rescue JSON::ParserError => e
|
|
85
|
+
@logger.error("Invalid JSON in request: #{e.message}")
|
|
86
|
+
json_rpc_error_response(400, -32700, "Parse error", nil)
|
|
87
|
+
rescue => e
|
|
88
|
+
@logger.error("Error processing message: #{e.message}")
|
|
89
|
+
@logger.error(e.backtrace.join("\n")) if e.backtrace
|
|
90
|
+
json_rpc_error_response(500, -32603, "Internal error", nil)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def valid_jsonrpc_message?(message)
|
|
95
|
+
return false unless message.is_a?(Hash)
|
|
96
|
+
return false unless message["jsonrpc"] == "2.0"
|
|
97
|
+
|
|
98
|
+
message.key?("method") || message.key?("result") || message.key?("error")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def method_not_allowed_response
|
|
102
|
+
[
|
|
103
|
+
405,
|
|
104
|
+
{ "Content-Type" => "application/json" },
|
|
105
|
+
[ JSON.generate({
|
|
106
|
+
jsonrpc: "2.0",
|
|
107
|
+
error: {
|
|
108
|
+
code: -32601,
|
|
109
|
+
message: "Method not allowed. This endpoint only supports POST requests."
|
|
110
|
+
},
|
|
111
|
+
id: nil
|
|
112
|
+
}) ]
|
|
113
|
+
]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def json_rpc_error_response(http_status, code, message, id)
|
|
117
|
+
[
|
|
118
|
+
http_status,
|
|
119
|
+
{ "Content-Type" => "application/json" },
|
|
120
|
+
[ JSON.generate({
|
|
121
|
+
jsonrpc: "2.0",
|
|
122
|
+
error: {
|
|
123
|
+
code: code,
|
|
124
|
+
message: message
|
|
125
|
+
},
|
|
126
|
+
id: id
|
|
127
|
+
}) ]
|
|
128
|
+
]
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -22,6 +22,12 @@ class Tidewave::Tools::ExecuteSqlQuery < Tidewave::Tools::Base
|
|
|
22
22
|
optional(:arguments).value(:array).description("The arguments to pass to the query. The query must contain corresponding parameter placeholders.")
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def @input_schema.json_schema
|
|
26
|
+
schema = super
|
|
27
|
+
schema[:properties][:arguments][:items] = {}
|
|
28
|
+
schema
|
|
29
|
+
end
|
|
30
|
+
|
|
25
31
|
RESULT_LIMIT = 50
|
|
26
32
|
|
|
27
33
|
def call(query:, arguments: [])
|
|
@@ -23,6 +23,12 @@ class Tidewave::Tools::ProjectEval < Tidewave::Tools::Base
|
|
|
23
23
|
optional(:json).hidden().filled(:bool).description("Whether to return the result as JSON with structured output containing result, success, stdout, and stderr fields. Defaults to false.")
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
def @input_schema.json_schema
|
|
27
|
+
schema = super
|
|
28
|
+
schema[:properties][:arguments][:items] = {}
|
|
29
|
+
schema
|
|
30
|
+
end
|
|
31
|
+
|
|
26
32
|
def call(code:, arguments: [], timeout: 30_000, json: false)
|
|
27
33
|
original_stdout = $stdout
|
|
28
34
|
original_stderr = $stderr
|
data/lib/tidewave/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tidewave
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yorick Jacquin
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2025-
|
|
12
|
+
date: 2025-11-25 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rails
|
|
@@ -31,14 +31,14 @@ dependencies:
|
|
|
31
31
|
requirements:
|
|
32
32
|
- - "~>"
|
|
33
33
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: 1.
|
|
34
|
+
version: 1.6.0
|
|
35
35
|
type: :runtime
|
|
36
36
|
prerelease: false
|
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
|
38
38
|
requirements:
|
|
39
39
|
- - "~>"
|
|
40
40
|
- !ruby/object:Gem::Version
|
|
41
|
-
version: 1.
|
|
41
|
+
version: 1.6.0
|
|
42
42
|
- !ruby/object:Gem::Dependency
|
|
43
43
|
name: rack
|
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -72,6 +72,7 @@ files:
|
|
|
72
72
|
- lib/tidewave/middleware.rb
|
|
73
73
|
- lib/tidewave/quiet_requests_middleware.rb
|
|
74
74
|
- lib/tidewave/railtie.rb
|
|
75
|
+
- lib/tidewave/streamable_http_transport.rb
|
|
75
76
|
- lib/tidewave/tools/base.rb
|
|
76
77
|
- lib/tidewave/tools/execute_sql_query.rb
|
|
77
78
|
- lib/tidewave/tools/get_docs.rb
|