rails-pg-extras-mcp 0.0.1 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2b38c7170279b40f497842e8b52683e94ec21ed567f9081ca4e0041fac00cc0
4
- data.tar.gz: d099ef809db25a1cadacc15faea3fc6bc7ba49dc8e60f985f7e1835ce962e127
3
+ metadata.gz: 4b1051245810be2fecf8e867a4d310f92afb59428b99946191a1e206cf5b44a0
4
+ data.tar.gz: 36da9617db4420bdc098e9562ddd5ae521b57b08bea97737a079cf1cae0e31c8
5
5
  SHA512:
6
- metadata.gz: c4fd58adbed92be275183094e26afb12ef156a214170f55c5e3e563d39e2fabdf59b4c7676401f97b820346e5173feef8630d41343d48d4d086961ece3dd4c93
7
- data.tar.gz: f8365a1506c36a6c9475bddadce4d97a0651b4a20e5a05100020ccfb49730f0691407e805503bc462403e24de9a800ad072984dfc8f452797cd11c560ae1a9c9
6
+ metadata.gz: 16765fe32563b12c1a8b2d7136c8f4a32556caafc52d223060c7af000b71fc747329e1e4a7296aafb73ba218b9aa57ae5a72aedcad1ebc68bc3ed9a03cbb5146
7
+ data.tar.gz: 2d0c7c8f2cb2317d02621752f6d82cadfd6c7ed82b364529bf15a6f5cf37c9f1f54ef7e13316139db8173a7f09562086afa626643d4cb6f5e4aca28be005b1f7
data/README.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # Rails PG Extras MCP [![Gem Version](https://badge.fury.io/rb/rails-pg-extras-mcp.svg)](https://badge.fury.io/rb/rails-pg-extras-mcp) [![GH Actions](https://github.com/pawurb/rails-pg-extras-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/pawurb/rails-pg-extras-mcp/actions)
2
2
 
3
+ MCP ([Model Context Protocol](https://modelcontextprotocol.io/introduction)) interface for [rails-pg-extras](https://github.com/pawurb/rails-pg-extras) gem. It enables PostgreSQL metadata and performance analysis with a simple LLM prompt.
3
4
 
4
- MCP ([Model Context Protocol](https://modelcontextprotocol.io/introduction)) interface for [rails-pg-extras](https://github.com/pawurb/rails-pg-extras) gem.
5
-
6
- A tool for those adventurous enough to connect LLMs directly to the database.
5
+ ![LLM interface](https://github.com/pawurb/rails-pg-extras/raw/main/pg-extras-mcp.png)
7
6
 
8
7
  ## Installation
9
8
 
@@ -12,15 +11,20 @@ bundle add rails-pg-extras
12
11
  bundle add rails-pg-extras-mcp
13
12
  ```
14
13
 
15
- Library supports MCP protocol via HTTP SSE interface.
14
+ The library supports MCP protocol via HTTP SSE interface.
16
15
 
17
16
  `config/routes.rb`
18
17
 
19
18
  ```ruby
20
- # Authentication is not yet supported
21
19
  mount RailsPgExtras.mcp_app, at: "pg-extras-mcp"
22
20
  ```
23
21
 
22
+ with optional authorization:
23
+
24
+ ```ruby
25
+ mount RailsPgExtras.mcp_app(auth_token: "secret"), at: "pg-extras-mcp"
26
+ ```
27
+
24
28
  Install [mcp-remote](https://github.com/geelen/mcp-remote):
25
29
 
26
30
  ```bash
@@ -36,7 +40,9 @@ and in your LLM of choice:
36
40
  "command": "npx",
37
41
  "args": [
38
42
  "mcp-remote",
39
- "http://localhost:3000/pg-extras-mcp/sse"
43
+ "http://localhost:3000/pg-extras-mcp/sse",
44
+ "--header",
45
+ "Authorization: secret"
40
46
  ]
41
47
  }
42
48
  }
@@ -45,8 +51,17 @@ and in your LLM of choice:
45
51
 
46
52
  You can now ask LLM questions about the metadata and performance metrics of your database.
47
53
 
48
- ![LLM interface](https://github.com/pawurb/rails-pg-extras/raw/main/pg-extras-mcp.png)
54
+ ## Optional EXPLAIN ANALYZE support
55
+
56
+ [`calls`](https://github.com/pawurb/rails-pg-extras?tab=readme-ov-file#calls) and [`outliers`](https://github.com/pawurb/rails-pg-extras?tab=readme-ov-file#outliers) methods return a list of bottleneck queries. LLM can get better insights into these queries by performing `EXPLAIN` and `EXPLAIN ANALYZE` analysis. MCP server exposes two optional methods for this purpose: `explain` and `explain_analyze`.
57
+
58
+ You can enable them by setting the following `ENV` variables:
59
+
60
+ `ENV['PG_EXTRAS_MCP_EXPLAIN_ENABLED'] = 'true'`
61
+ `ENV['PG_EXTRAS_MCP_EXPLAIN_ANALYZE_ENABLED'] = 'true'`
62
+
63
+ Enabling these features means that an LLM, can run arbitrary queries in your database. The execution context is wrapped in a transaction and rolled back, so, in theory, any data modification should not be possible. But it's advised to configure a read-only permission if you want to use these features. By specifying `ENV['RAILS_PG_EXTRAS_DATABASE_URL']` you can overwrite the default Rails ActiveRecord database connection to restrict an access scope.
49
64
 
50
65
  ## Status
51
66
 
52
- Project is in an early beta, so proceed with caution.
67
+ The project is in an early beta, so proceed with caution.
data/Rakefile CHANGED
@@ -2,4 +2,3 @@ require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
-
@@ -63,6 +63,68 @@ class DiagnoseTool < FastMcp::Tool
63
63
  end
64
64
  end
65
65
 
66
+ class ExplainBaseTool < FastMcp::Tool
67
+ DENYLIST = %w[
68
+ delete,
69
+ insert,
70
+ update,
71
+ truncate,
72
+ drop,
73
+ alter,
74
+ create,
75
+ grant,
76
+ begin,
77
+ commit,
78
+ ;
79
+ ]
80
+
81
+ arguments do
82
+ required(:query).filled(:string).description("The query to debug")
83
+ end
84
+
85
+ def call(query:)
86
+ connection = RailsPgExtras.connection
87
+
88
+ if DENYLIST.any? { |deny| query.downcase.include?(deny) }
89
+ raise "This query is not allowed. It contains a denied keyword. Denylist: #{DENYLIST.join(", ")}"
90
+ end
91
+
92
+ connection.execute("BEGIN")
93
+ result = connection.execute("#{query}")
94
+ connection.execute("ROLLBACK")
95
+
96
+ result.to_a
97
+ end
98
+ end
99
+
100
+ class ExplainTool < ExplainBaseTool
101
+ description "EXPLAIN a query. It must be an SQL string, without the EXPLAIN prefix"
102
+
103
+ def self.name
104
+ "explain_analyze"
105
+ end
106
+
107
+ def call(query:)
108
+ if query.downcase.include?("analyze")
109
+ raise "This query is not allowed. It contains a denied ANALYZE keyword."
110
+ end
111
+
112
+ super(query: "EXPLAIN #{query}")
113
+ end
114
+ end
115
+
116
+ class ExplainAnalyzeTool < ExplainBaseTool
117
+ description "EXPLAIN ANALYZE a query. It must be an SQL string, without the EXPLAIN ANALYZE prefix"
118
+
119
+ def self.name
120
+ "explain_analyze"
121
+ end
122
+
123
+ def call(query:)
124
+ super(query: "EXPLAIN ANALYZE #{query}")
125
+ end
126
+ end
127
+
66
128
  class ReadmeResource < FastMcp::Resource
67
129
  uri "https://raw.githubusercontent.com/pawurb/rails-pg-extras/refs/heads/main/README.md"
68
130
  resource_name "README"
@@ -76,23 +138,35 @@ end
76
138
 
77
139
  module RailsPgExtrasMcp
78
140
  class App
79
- def self.build
141
+ def self.build(auth_token: nil)
80
142
  app = lambda do |_env|
81
143
  [200, { "Content-Type" => "text/html" },
82
144
  ["<html><body><h1>Hello from Rack!</h1><p>This is a simple Rack app with MCP middleware.</p></body></html>"]]
83
145
  end
84
146
 
85
- # Create the MCP middleware
86
- mcp_app = FastMcp.rack_middleware(
87
- app,
88
- name: "rails-pg-extras-mcp", version: RailsPgExtrasMcp::VERSION,
147
+ opts = {
148
+ name: "rails-pg-extras-mcp",
149
+ version: RailsPgExtrasMcp::VERSION,
89
150
  path_prefix: "/pg-extras-mcp",
90
151
  logger: Logger.new($stdout),
91
- ) do |server|
152
+ }
153
+
154
+ if auth_token.present?
155
+ opts[:auth_token] = auth_token
156
+ end
157
+
158
+ rack_method_name = auth_token.present? ? :authenticated_rack_middleware : :rack_middleware
159
+
160
+ # Create the MCP middleware
161
+ mcp_app = FastMcp.public_send(rack_method_name,
162
+ app,
163
+ **opts) do |server|
92
164
  server.register_tools(DiagnoseTool)
93
165
  server.register_tools(MissingFkConstraintsTool)
94
166
  server.register_tools(MissingFkIndexesTool)
95
167
  server.register_tools(*QUERY_TOOL_CLASSES)
168
+ server.register_tools(ExplainTool) if ENV["PG_EXTRAS_MCP_EXPLAIN_ENABLED"] == "true"
169
+ server.register_tools(ExplainAnalyzeTool) if ENV["PG_EXTRAS_MCP_EXPLAIN_ANALYZE_ENABLED"] == "true"
96
170
 
97
171
  server.register_resource(ReadmeResource)
98
172
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsPgExtrasMcp
4
- VERSION = "0.0.1"
4
+ VERSION = "0.1.1"
5
5
  end
data/spec/smoke_spec.rb CHANGED
@@ -3,67 +3,10 @@
3
3
  require "spec_helper"
4
4
  require "rails-pg-extras"
5
5
 
6
- describe RailsPgExtras do
7
- SKIP_QUERIES = %i[
8
- kill_all
9
- table_schema
10
- table_foreign_keys
11
- ]
12
-
13
- RailsPgExtras::QUERIES.reject { |q| SKIP_QUERIES.include?(q) }.each do |query_name|
14
- it "#{query_name} query can be executed" do
15
- expect do
16
- RailsPgExtras.run_query(
17
- query_name: query_name,
18
- in_format: :hash,
19
- )
20
- end.not_to raise_error
21
- end
22
- end
23
-
24
- it "runs the custom methods" do
25
- expect do
26
- RailsPgExtras.diagnose(in_format: :hash)
27
- end.not_to raise_error
28
-
29
- expect do
30
- RailsPgExtras.index_info(in_format: :hash)
31
- end.not_to raise_error
32
-
33
- expect do
34
- RailsPgExtras.table_info(in_format: :hash)
35
- end.not_to raise_error
36
- end
37
-
38
- it "collecting queries data works" do
39
- output = RailsPgExtras.measure_queries { RailsPgExtras.diagnose(in_format: :hash) }
40
- expect(output.fetch(:count) > 0).to eq(true)
41
- end
42
-
43
- it "supports custom RAILS_PG_EXTRAS_DATABASE_URL" do
44
- ENV["RAILS_PG_EXTRAS_DATABASE_URL"] = ENV["DATABASE_URL"]
45
- puts ENV["RAILS_PG_EXTRAS_DATABASE_URL"]
46
-
47
- expect do
48
- RailsPgExtras.calls
49
- end.not_to raise_error
50
-
51
- ENV["RAILS_PG_EXTRAS_DATABASE_URL"] = nil
52
- end
53
-
54
- describe "missing_fk_indexes" do
55
- it "works" do
56
- expect {
57
- RailsPgExtras.missing_fk_indexes
58
- }.not_to raise_error
59
- end
60
- end
61
-
62
- describe "missing_fk_constraints" do
63
- it "works" do
64
- expect {
65
- RailsPgExtras.missing_fk_constraints
66
- }.not_to raise_error
67
- end
6
+ describe RailsPgExtrasMcp do
7
+ it "works" do
8
+ expect {
9
+ RailsPgExtrasMcp::App.build
10
+ }.not_to raise_error
68
11
  end
69
12
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-pg-extras-mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - pawurb
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-06-12 00:00:00.000000000 Z
11
+ date: 2025-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails-pg-extras
@@ -112,7 +112,6 @@ files:
112
112
  - lib/rails-pg-extras-mcp.rb
113
113
  - lib/rails_pg_extras_mcp/version.rb
114
114
  - pg-extras-mcp.png
115
- - rails-pg-extras-diagnose.png
116
115
  - rails-pg-extras-mcp.gemspec
117
116
  - spec/smoke_spec.rb
118
117
  - spec/spec_helper.rb
Binary file