parse-stack-next 4.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.
Files changed (178) hide show
  1. checksums.yaml +7 -0
  2. data/.bundle/config +2 -0
  3. data/.env.sample +112 -0
  4. data/.env.test +10 -0
  5. data/.github/workflows/ruby.yml +36 -0
  6. data/.gitignore +49 -0
  7. data/.ruby-version +1 -0
  8. data/.solargraph.yml +22 -0
  9. data/CHANGELOG.md +5816 -0
  10. data/Gemfile +30 -0
  11. data/Gemfile.lock +175 -0
  12. data/LICENSE.txt +23 -0
  13. data/Makefile +63 -0
  14. data/README.md +5655 -0
  15. data/Rakefile +573 -0
  16. data/bin/console +38 -0
  17. data/bin/parse-console +136 -0
  18. data/bin/server +17 -0
  19. data/bin/setup +7 -0
  20. data/config/parse-config.json +12 -0
  21. data/docs/TEST_SERVER.md +271 -0
  22. data/docs/_config.yml +1 -0
  23. data/docs/mcp_guide.md +3484 -0
  24. data/docs/mongodb_direct_guide.md +1348 -0
  25. data/docs/mongodb_index_optimization_guide.md +631 -0
  26. data/examples/transaction_example.rb +219 -0
  27. data/lib/parse/acl_scope.rb +728 -0
  28. data/lib/parse/agent/cancellation_token.rb +80 -0
  29. data/lib/parse/agent/constraint_translator.rb +480 -0
  30. data/lib/parse/agent/describe.rb +420 -0
  31. data/lib/parse/agent/errors.rb +133 -0
  32. data/lib/parse/agent/mcp_client.rb +557 -0
  33. data/lib/parse/agent/mcp_dispatcher.rb +1023 -0
  34. data/lib/parse/agent/mcp_rack_app.rb +1143 -0
  35. data/lib/parse/agent/mcp_server.rb +376 -0
  36. data/lib/parse/agent/metadata_audit.rb +259 -0
  37. data/lib/parse/agent/metadata_dsl.rb +733 -0
  38. data/lib/parse/agent/metadata_registry.rb +794 -0
  39. data/lib/parse/agent/pipeline_validator.rb +82 -0
  40. data/lib/parse/agent/prompts.rb +351 -0
  41. data/lib/parse/agent/rate_limiter.rb +158 -0
  42. data/lib/parse/agent/relation_graph.rb +162 -0
  43. data/lib/parse/agent/result_formatter.rb +453 -0
  44. data/lib/parse/agent/tools.rb +5489 -0
  45. data/lib/parse/agent.rb +3249 -0
  46. data/lib/parse/api/aggregate.rb +79 -0
  47. data/lib/parse/api/all.rb +26 -0
  48. data/lib/parse/api/analytics.rb +18 -0
  49. data/lib/parse/api/batch.rb +33 -0
  50. data/lib/parse/api/cloud_functions.rb +58 -0
  51. data/lib/parse/api/config.rb +125 -0
  52. data/lib/parse/api/files.rb +29 -0
  53. data/lib/parse/api/hooks.rb +117 -0
  54. data/lib/parse/api/objects.rb +146 -0
  55. data/lib/parse/api/path_segment.rb +75 -0
  56. data/lib/parse/api/push.rb +20 -0
  57. data/lib/parse/api/schema.rb +49 -0
  58. data/lib/parse/api/server.rb +50 -0
  59. data/lib/parse/api/sessions.rb +24 -0
  60. data/lib/parse/api/users.rb +250 -0
  61. data/lib/parse/atlas_search/index_manager.rb +353 -0
  62. data/lib/parse/atlas_search/result.rb +204 -0
  63. data/lib/parse/atlas_search/search_builder.rb +604 -0
  64. data/lib/parse/atlas_search/session.rb +253 -0
  65. data/lib/parse/atlas_search.rb +995 -0
  66. data/lib/parse/client/authentication.rb +97 -0
  67. data/lib/parse/client/batch.rb +234 -0
  68. data/lib/parse/client/body_builder.rb +240 -0
  69. data/lib/parse/client/caching.rb +203 -0
  70. data/lib/parse/client/logging.rb +293 -0
  71. data/lib/parse/client/profiling.rb +181 -0
  72. data/lib/parse/client/protocol.rb +91 -0
  73. data/lib/parse/client/request.rb +233 -0
  74. data/lib/parse/client/response.rb +208 -0
  75. data/lib/parse/client.rb +1104 -0
  76. data/lib/parse/clp_scope.rb +361 -0
  77. data/lib/parse/live_query/circuit_breaker.rb +256 -0
  78. data/lib/parse/live_query/client.rb +1001 -0
  79. data/lib/parse/live_query/configuration.rb +224 -0
  80. data/lib/parse/live_query/event.rb +115 -0
  81. data/lib/parse/live_query/event_queue.rb +272 -0
  82. data/lib/parse/live_query/health_monitor.rb +214 -0
  83. data/lib/parse/live_query/logging.rb +149 -0
  84. data/lib/parse/live_query/subscription.rb +294 -0
  85. data/lib/parse/live_query.rb +163 -0
  86. data/lib/parse/lookup_rewriter.rb +445 -0
  87. data/lib/parse/model/acl.rb +968 -0
  88. data/lib/parse/model/associations/belongs_to.rb +275 -0
  89. data/lib/parse/model/associations/collection_proxy.rb +435 -0
  90. data/lib/parse/model/associations/has_many.rb +597 -0
  91. data/lib/parse/model/associations/has_one.rb +158 -0
  92. data/lib/parse/model/associations/pointer_collection_proxy.rb +134 -0
  93. data/lib/parse/model/associations/relation_collection_proxy.rb +177 -0
  94. data/lib/parse/model/bytes.rb +62 -0
  95. data/lib/parse/model/classes/audience.rb +262 -0
  96. data/lib/parse/model/classes/installation.rb +363 -0
  97. data/lib/parse/model/classes/job_schedule.rb +153 -0
  98. data/lib/parse/model/classes/job_status.rb +264 -0
  99. data/lib/parse/model/classes/product.rb +75 -0
  100. data/lib/parse/model/classes/push_status.rb +263 -0
  101. data/lib/parse/model/classes/role.rb +751 -0
  102. data/lib/parse/model/classes/session.rb +201 -0
  103. data/lib/parse/model/classes/user.rb +943 -0
  104. data/lib/parse/model/clp.rb +544 -0
  105. data/lib/parse/model/core/actions.rb +1268 -0
  106. data/lib/parse/model/core/builder.rb +139 -0
  107. data/lib/parse/model/core/create_lock.rb +386 -0
  108. data/lib/parse/model/core/describe.rb +382 -0
  109. data/lib/parse/model/core/enhanced_change_tracking.rb +159 -0
  110. data/lib/parse/model/core/errors.rb +38 -0
  111. data/lib/parse/model/core/fetching.rb +566 -0
  112. data/lib/parse/model/core/field_guards.rb +220 -0
  113. data/lib/parse/model/core/indexing.rb +382 -0
  114. data/lib/parse/model/core/parse_reference.rb +407 -0
  115. data/lib/parse/model/core/properties.rb +809 -0
  116. data/lib/parse/model/core/querying.rb +491 -0
  117. data/lib/parse/model/core/schema.rb +202 -0
  118. data/lib/parse/model/core/search_indexing.rb +174 -0
  119. data/lib/parse/model/date.rb +88 -0
  120. data/lib/parse/model/email.rb +213 -0
  121. data/lib/parse/model/file.rb +527 -0
  122. data/lib/parse/model/geojson.rb +271 -0
  123. data/lib/parse/model/geopoint.rb +261 -0
  124. data/lib/parse/model/model.rb +260 -0
  125. data/lib/parse/model/object.rb +2068 -0
  126. data/lib/parse/model/phone.rb +520 -0
  127. data/lib/parse/model/pointer.rb +443 -0
  128. data/lib/parse/model/polygon.rb +406 -0
  129. data/lib/parse/model/push.rb +975 -0
  130. data/lib/parse/model/shortnames.rb +8 -0
  131. data/lib/parse/model/time_zone.rb +141 -0
  132. data/lib/parse/model/validations/uniqueness_validator.rb +97 -0
  133. data/lib/parse/model/validations.rb +96 -0
  134. data/lib/parse/mongodb.rb +2300 -0
  135. data/lib/parse/pipeline_security.rb +554 -0
  136. data/lib/parse/query/constraint.rb +198 -0
  137. data/lib/parse/query/constraints.rb +3279 -0
  138. data/lib/parse/query/cursor.rb +434 -0
  139. data/lib/parse/query/n_plus_one_detector.rb +445 -0
  140. data/lib/parse/query/operation.rb +104 -0
  141. data/lib/parse/query/ordering.rb +66 -0
  142. data/lib/parse/query.rb +7028 -0
  143. data/lib/parse/schema/index_migrator.rb +291 -0
  144. data/lib/parse/schema/search_index_migrator.rb +289 -0
  145. data/lib/parse/schema.rb +494 -0
  146. data/lib/parse/stack/generators/rails.rb +40 -0
  147. data/lib/parse/stack/generators/templates/model.erb +51 -0
  148. data/lib/parse/stack/generators/templates/model_installation.rb +4 -0
  149. data/lib/parse/stack/generators/templates/model_role.rb +4 -0
  150. data/lib/parse/stack/generators/templates/model_session.rb +4 -0
  151. data/lib/parse/stack/generators/templates/model_user.rb +11 -0
  152. data/lib/parse/stack/generators/templates/parse.rb +12 -0
  153. data/lib/parse/stack/generators/templates/webhooks.rb +10 -0
  154. data/lib/parse/stack/railtie.rb +18 -0
  155. data/lib/parse/stack/tasks.rb +563 -0
  156. data/lib/parse/stack/version.rb +11 -0
  157. data/lib/parse/stack.rb +455 -0
  158. data/lib/parse/two_factor_auth/user_extension.rb +449 -0
  159. data/lib/parse/two_factor_auth.rb +310 -0
  160. data/lib/parse/webhooks/payload.rb +360 -0
  161. data/lib/parse/webhooks/registration.rb +199 -0
  162. data/lib/parse/webhooks/replay_protection.rb +189 -0
  163. data/lib/parse/webhooks.rb +510 -0
  164. data/lib/parse-stack-next.rb +5 -0
  165. data/lib/parse-stack.rb +5 -0
  166. data/parse-stack-next.gemspec +82 -0
  167. data/parse-stack.png +0 -0
  168. data/scripts/debug-ips.js +35 -0
  169. data/scripts/docker/Dockerfile.parse +13 -0
  170. data/scripts/docker/atlas-init.js +284 -0
  171. data/scripts/docker/docker-compose.atlas.yml +76 -0
  172. data/scripts/docker/docker-compose.test.yml +106 -0
  173. data/scripts/docker/mongo-init.js +21 -0
  174. data/scripts/eval_mcp_with_lm_studio.rb +274 -0
  175. data/scripts/start-parse.sh +90 -0
  176. data/scripts/start_mcp_server.rb +78 -0
  177. data/scripts/test_server_connection.rb +82 -0
  178. metadata +377 -0
@@ -0,0 +1,274 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # frozen_string_literal: true
4
+
5
+ # Evaluate MCP Server with LM Studio
6
+ #
7
+ # This script connects LM Studio to the Parse MCP Server, allowing the
8
+ # LLM to query Parse data using the MCP tools.
9
+ #
10
+ # Usage:
11
+ # ruby scripts/eval_mcp_with_lm_studio.rb
12
+ #
13
+ # Prerequisites:
14
+ # - LM Studio running at http://127.0.0.1:1234
15
+ # - MCP Server running at http://localhost:3001
16
+ # - Parse Server running with test data
17
+
18
+ require "net/http"
19
+ require "json"
20
+ require "uri"
21
+
22
+ class MCPLMStudioEvaluator
23
+ LM_STUDIO_URL = ENV["LM_STUDIO_URL"] || "http://127.0.0.1:1234"
24
+ MCP_SERVER_URL = ENV["MCP_SERVER_URL"] || "http://localhost:3001"
25
+
26
+ def initialize
27
+ @conversation = []
28
+ @tool_definitions = nil
29
+ end
30
+
31
+ # Fetch tool definitions from MCP server
32
+ def fetch_tools
33
+ uri = URI("#{MCP_SERVER_URL}/tools")
34
+ response = Net::HTTP.get_response(uri)
35
+
36
+ unless response.is_a?(Net::HTTPSuccess)
37
+ raise "Failed to fetch tools: #{response.code} #{response.message}"
38
+ end
39
+
40
+ tools = JSON.parse(response.body)
41
+
42
+ # Convert MCP format to OpenAI function calling format
43
+ @tool_definitions = tools.map do |tool|
44
+ {
45
+ type: "function",
46
+ function: {
47
+ name: tool["name"],
48
+ description: tool["description"],
49
+ parameters: tool["inputSchema"]
50
+ }
51
+ }
52
+ end
53
+
54
+ puts "Loaded #{@tool_definitions.size} tools from MCP server"
55
+ @tool_definitions
56
+ end
57
+
58
+ # Call a tool via MCP server
59
+ def call_mcp_tool(tool_name, arguments)
60
+ uri = URI("#{MCP_SERVER_URL}/mcp")
61
+ http = Net::HTTP.new(uri.host, uri.port)
62
+
63
+ request = Net::HTTP::Post.new(uri.path)
64
+ request["Content-Type"] = "application/json"
65
+ request.body = JSON.generate({
66
+ jsonrpc: "2.0",
67
+ id: rand(10000),
68
+ method: "tools/call",
69
+ params: {
70
+ name: tool_name,
71
+ arguments: arguments
72
+ }
73
+ })
74
+
75
+ response = http.request(request)
76
+ result = JSON.parse(response.body)
77
+
78
+ if result["error"]
79
+ { error: result["error"]["message"] }
80
+ else
81
+ result["result"]
82
+ end
83
+ end
84
+
85
+ # Send a message to LM Studio
86
+ def chat_with_lm(user_message, tools: true)
87
+ uri = URI("#{LM_STUDIO_URL}/v1/chat/completions")
88
+ http = Net::HTTP.new(uri.host, uri.port)
89
+ http.read_timeout = 300 # LLMs can be slow, especially larger models
90
+ http.open_timeout = 30
91
+
92
+ @conversation << { role: "user", content: user_message }
93
+
94
+ request_body = {
95
+ model: "qwen2.5-32b-instruct",
96
+ messages: @conversation,
97
+ temperature: 0.1,
98
+ max_tokens: 2000
99
+ }
100
+
101
+ # Add tools if available and requested
102
+ if tools && @tool_definitions
103
+ request_body[:tools] = @tool_definitions
104
+ request_body[:tool_choice] = "auto"
105
+ end
106
+
107
+ request = Net::HTTP::Post.new(uri.path)
108
+ request["Content-Type"] = "application/json"
109
+ request.body = JSON.generate(request_body)
110
+
111
+ puts "\n>>> Sending to LM Studio..."
112
+ response = http.request(request)
113
+
114
+ unless response.is_a?(Net::HTTPSuccess)
115
+ raise "LM Studio error: #{response.code} #{response.message}\n#{response.body}"
116
+ end
117
+
118
+ result = JSON.parse(response.body)
119
+ assistant_message = result["choices"][0]["message"]
120
+
121
+ @conversation << assistant_message
122
+
123
+ assistant_message
124
+ end
125
+
126
+ # Check if message has actual tool calls
127
+ def has_tool_calls?(message)
128
+ return false unless message
129
+ tool_calls = message["tool_calls"]
130
+ return false unless tool_calls.is_a?(Array) && !tool_calls.empty?
131
+ tool_calls.any? { |tc| tc["function"] && tc["function"]["name"] }
132
+ end
133
+
134
+ # Process tool calls from the LLM
135
+ def process_tool_calls(message)
136
+ tool_calls = message["tool_calls"]
137
+ return nil unless tool_calls && !tool_calls.empty?
138
+
139
+ tool_results = []
140
+
141
+ tool_calls.each do |tool_call|
142
+ function = tool_call["function"]
143
+ next unless function && function["name"]
144
+
145
+ tool_name = function["name"]
146
+ arguments = JSON.parse(function["arguments"] || "{}")
147
+
148
+ puts "\n🔧 LLM calling tool: #{tool_name}"
149
+ puts " Arguments: #{JSON.pretty_generate(arguments)}"
150
+
151
+ result = call_mcp_tool(tool_name, arguments)
152
+
153
+ puts " Result preview: #{result.to_s[0..200]}..."
154
+
155
+ tool_results << {
156
+ role: "tool",
157
+ tool_call_id: tool_call["id"],
158
+ content: JSON.generate(result)
159
+ }
160
+ end
161
+
162
+ return message if tool_results.empty?
163
+
164
+ # Add tool results to conversation
165
+ tool_results.each { |r| @conversation << r }
166
+
167
+ # Get LLM's response after tool calls - allow more tool calls
168
+ chat_with_lm("", tools: true)
169
+ end
170
+
171
+ # Run a full evaluation with a user prompt
172
+ def evaluate(prompt)
173
+ puts "=" * 60
174
+ puts "MCP + LM Studio Evaluation"
175
+ puts "=" * 60
176
+ puts "\nLM Studio: #{LM_STUDIO_URL}"
177
+ puts "MCP Server: #{MCP_SERVER_URL}"
178
+ puts "\nUser prompt: #{prompt}"
179
+ puts "=" * 60
180
+
181
+ # Initialize
182
+ fetch_tools
183
+
184
+ # Add system message
185
+ @conversation = [{
186
+ role: "system",
187
+ content: <<~SYSTEM
188
+ You are a helpful assistant with access to a Parse database.
189
+ Use the available tools to answer questions about the data.
190
+ Always start by getting the schema if you need to understand the database structure.
191
+ When querying, be specific and use appropriate constraints.
192
+ SYSTEM
193
+ }]
194
+
195
+ # Send user message
196
+ response = chat_with_lm(prompt)
197
+
198
+ # Handle tool calls in a loop
199
+ max_iterations = 5
200
+ iterations = 0
201
+
202
+ while has_tool_calls?(response) && iterations < max_iterations
203
+ iterations += 1
204
+ puts "\n--- Tool call iteration #{iterations} ---"
205
+ new_response = process_tool_calls(response)
206
+ break if new_response.nil? || new_response == response
207
+ response = new_response
208
+ end
209
+
210
+ puts "\n" + "=" * 60
211
+ puts "Final Response:"
212
+ puts "=" * 60
213
+ puts response["content"]
214
+ puts "=" * 60
215
+
216
+ response["content"]
217
+ end
218
+
219
+ # Check if services are available
220
+ def check_services
221
+ puts "Checking services..."
222
+
223
+ # Check LM Studio
224
+ begin
225
+ uri = URI("#{LM_STUDIO_URL}/v1/models")
226
+ response = Net::HTTP.get_response(uri)
227
+ if response.is_a?(Net::HTTPSuccess)
228
+ models = JSON.parse(response.body)
229
+ puts "✓ LM Studio is running"
230
+ puts " Available models: #{models["data"]&.map { |m| m["id"] }&.join(", ") || "unknown"}"
231
+ else
232
+ puts "✗ LM Studio returned: #{response.code}"
233
+ return false
234
+ end
235
+ rescue => e
236
+ puts "✗ Cannot connect to LM Studio at #{LM_STUDIO_URL}: #{e.message}"
237
+ return false
238
+ end
239
+
240
+ # Check MCP Server
241
+ begin
242
+ uri = URI("#{MCP_SERVER_URL}/health")
243
+ response = Net::HTTP.get_response(uri)
244
+ if response.is_a?(Net::HTTPSuccess)
245
+ puts "✓ MCP Server is running"
246
+ else
247
+ puts "✗ MCP Server returned: #{response.code}"
248
+ return false
249
+ end
250
+ rescue => e
251
+ puts "✗ Cannot connect to MCP Server at #{MCP_SERVER_URL}: #{e.message}"
252
+ return false
253
+ end
254
+
255
+ puts ""
256
+ true
257
+ end
258
+ end
259
+
260
+ # Main execution
261
+ if __FILE__ == $0
262
+ evaluator = MCPLMStudioEvaluator.new
263
+
264
+ unless evaluator.check_services
265
+ puts "\nPlease ensure both LM Studio and MCP Server are running."
266
+ puts "Start MCP Server with: ruby scripts/start_mcp_server.rb"
267
+ exit 1
268
+ end
269
+
270
+ # Default prompt if none provided
271
+ prompt = ARGV[0] || "What tables are in the database? Show me a sample of data from one of them."
272
+
273
+ evaluator.evaluate(prompt)
274
+ end
@@ -0,0 +1,90 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ # Parse Server startup script — TEST STACK ONLY.
5
+ #
6
+ # This script is bind-mounted into the parse-server container started by
7
+ # scripts/docker/docker-compose.test.yml for `rake test:integration`. It
8
+ # is NOT a template for production deployment.
9
+ #
10
+ # Required environment variables (provided by the test docker-compose
11
+ # via `${PARSE_APP_ID}`, `${PARSE_MASTER_KEY}`, … which themselves fall
12
+ # through to the compose's own defaults). The script aborts immediately
13
+ # if any required variable is missing — preventing a silent boot with
14
+ # placeholder credentials when an env-var name has drifted or a secret-
15
+ # manager binding fails.
16
+
17
+ echo "=== Parse Server Startup Script (test stack) ==="
18
+
19
+ # Hard-fail on any required env var being missing. The compose file
20
+ # is expected to supply each one via env interpolation; if it's not,
21
+ # we want a loud failure here rather than a silent boot with whatever
22
+ # default Parse Server itself would have applied.
23
+ require_env() {
24
+ eval "value=\${$1:-}"
25
+ if [ -z "$value" ]; then
26
+ echo "[start-parse] Refusing to start: required environment variable $1 is not set." >&2
27
+ exit 1
28
+ fi
29
+ }
30
+
31
+ require_env PARSE_SERVER_APPLICATION_ID
32
+ require_env PARSE_SERVER_MASTER_KEY
33
+ require_env PARSE_SERVER_DATABASE_URI
34
+
35
+ # masterKeyIps restricts which client IPs are allowed to present the
36
+ # master key. Default to loopback only. To allow other ranges (e.g. a
37
+ # private VPC subnet hosting the Ruby app dynos), override before
38
+ # invoking this script:
39
+ #
40
+ # PARSE_SERVER_MASTER_KEY_IPS="10.0.0.0/8,::1/128" ./start-parse.sh
41
+ #
42
+ # DO NOT set this to "0.0.0.0/0,::/0" in any environment reachable from
43
+ # the public internet. Doing so lets any caller that knows the master
44
+ # key bypass every ACL and CLP from any source IP.
45
+ export PARSE_SERVER_MASTER_KEY_IPS="${PARSE_SERVER_MASTER_KEY_IPS:-127.0.0.1/32,::1/128}"
46
+
47
+ # Optional REST API key — accept a default of empty when not provided
48
+ # by the compose file. Parse Server tolerates an unset REST key.
49
+ export PARSE_SERVER_REST_API_KEY="${PARSE_SERVER_REST_API_KEY:-}"
50
+ export PARSE_SERVER_MOUNT_PATH="${PARSE_SERVER_MOUNT_PATH:-/parse}"
51
+ export PARSE_SERVER_CLOUD="${PARSE_SERVER_CLOUD:-/parse-server/cloud/main.js}"
52
+ export PARSE_SERVER_LOG_LEVEL="${PARSE_SERVER_LOG_LEVEL:-info}"
53
+ export PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION="${PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION:-true}"
54
+ # Accept client-supplied objectId on create. Required by the
55
+ # `parse_reference precompute: true` DSL, which client-generates an
56
+ # objectId in a before_create callback and embeds it in the initial
57
+ # POST body. The SDK only forwards the client objectId when the save
58
+ # runs with master-key authority; the server flag is global, so any
59
+ # additional non-master client-objectId enforcement must be applied
60
+ # in cloud code (see lib/parse/model/core/parse_reference.rb).
61
+ export PARSE_SERVER_ALLOW_CUSTOM_OBJECT_ID="${PARSE_SERVER_ALLOW_CUSTOM_OBJECT_ID:-true}"
62
+
63
+ # LiveQuery configuration via environment variables
64
+ export PARSE_SERVER_LIVE_QUERY="${PARSE_SERVER_LIVE_QUERY:-{\"classNames\":[\"Song\",\"Album\",\"User\",\"_User\",\"TestLiveQuery\"]}}"
65
+ export PARSE_SERVER_START_LIVE_QUERY_SERVER="${PARSE_SERVER_START_LIVE_QUERY_SERVER:-true}"
66
+
67
+ echo "Environment configured:"
68
+ echo " PARSE_SERVER_APPLICATION_ID: $PARSE_SERVER_APPLICATION_ID"
69
+ echo " PARSE_SERVER_LIVE_QUERY: $PARSE_SERVER_LIVE_QUERY"
70
+ echo " PARSE_SERVER_START_LIVE_QUERY_SERVER: $PARSE_SERVER_START_LIVE_QUERY_SERVER"
71
+
72
+ # Start Parse Server
73
+ echo "Starting Parse Server..."
74
+ echo "PATH: $PATH"
75
+ echo "Looking for parse-server..."
76
+ which node
77
+ ls -la /parse-server/
78
+
79
+ # Try different ways to start parse-server
80
+ if [ -f "/parse-server/bin/parse-server" ]; then
81
+ echo "Using /parse-server/bin/parse-server"
82
+ exec /parse-server/bin/parse-server
83
+ elif [ -f "/usr/src/app/bin/parse-server" ]; then
84
+ echo "Using /usr/src/app/bin/parse-server"
85
+ exec /usr/src/app/bin/parse-server
86
+ else
87
+ echo "Trying with node and index.js"
88
+ cd /parse-server
89
+ exec node ./bin/parse-server
90
+ fi
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # frozen_string_literal: true
4
+
5
+ # Start the Parse MCP Server for AI agent integration.
6
+ #
7
+ # Required environment variables (no defaults — the script aborts if any
8
+ # is missing):
9
+ # PARSE_SERVER_URL - Parse Server URL (e.g. http://localhost:2337/parse)
10
+ # PARSE_APP_ID - Application ID
11
+ # PARSE_MASTER_KEY - Master Key
12
+ #
13
+ # Optional:
14
+ # PARSE_API_KEY - REST API Key (defaults unset; configure if your
15
+ # deployment requires it)
16
+ # MCP_PORT - MCP Server port (default: 3001)
17
+ # MCP_API_KEY - Bearer token gating the /mcp endpoint when
18
+ # binding to a non-loopback host
19
+ #
20
+ # Why no fallback values: previously this script accepted
21
+ # `ENV["PARSE_APP_ID"] || "myAppId"` and the equivalent for the master
22
+ # key. A deployment that forgot to set the env var (typo'd name, missing
23
+ # secret manager binding, container startup race) would silently boot
24
+ # with the placeholder credentials documented in the README — credentials
25
+ # that any reader of this repo knows. Failing closed here is a one-line
26
+ # safety net against that class of foot-gun.
27
+
28
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
29
+
30
+ require "parse-stack"
31
+
32
+ # Hard-fail on any missing required env var. The error message names the
33
+ # variable so the operator can fix it without having to re-read the
34
+ # header comment.
35
+ def require_env!(name)
36
+ value = ENV[name]
37
+ if value.nil? || value.empty?
38
+ abort "[start_mcp_server] Refusing to start: required environment variable #{name} is not set."
39
+ end
40
+ value
41
+ end
42
+
43
+ server_url = require_env!("PARSE_SERVER_URL")
44
+ application_id = require_env!("PARSE_APP_ID")
45
+ master_key = require_env!("PARSE_MASTER_KEY")
46
+ api_key = ENV["PARSE_API_KEY"] # optional
47
+
48
+ # Configure Parse client
49
+ Parse.setup(
50
+ server_url: server_url,
51
+ application_id: application_id,
52
+ api_key: api_key,
53
+ master_key: master_key,
54
+ )
55
+
56
+ port = (ENV["MCP_PORT"] || 3001).to_i
57
+
58
+ puts "=" * 60
59
+ puts "Parse MCP Server"
60
+ puts "=" * 60
61
+ puts ""
62
+ puts "Parse Server: #{Parse.client.server_url}"
63
+ puts "MCP Port: #{port}"
64
+ puts ""
65
+ puts "Endpoints:"
66
+ puts " Health: http://localhost:#{port}/health"
67
+ puts " Tools: http://localhost:#{port}/tools"
68
+ puts " MCP: http://localhost:#{port}/mcp (POST)"
69
+ puts ""
70
+ puts "For LM Studio, configure the API endpoint as:"
71
+ puts " http://localhost:#{port}/mcp"
72
+ puts ""
73
+ puts "=" * 60
74
+
75
+ # Enable MCP server feature (experimental)
76
+ Parse.mcp_server_enabled = true
77
+ Parse::Agent.enable_mcp!(port: port)
78
+ Parse::Agent::MCPServer.run(port: port)
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/parse/stack'
3
+ require_relative '../test/support/test_server'
4
+ require_relative '../test/support/docker_helper'
5
+
6
+ puts "Parse Stack Test Server Connection Test"
7
+ puts "=" * 40
8
+
9
+ # Try to start Docker containers
10
+ puts "\n1. Starting Docker containers..."
11
+ if Parse::Test::DockerHelper.start!
12
+ puts "✓ Docker containers started successfully"
13
+ else
14
+ puts "✗ Failed to start Docker containers"
15
+ exit 1
16
+ end
17
+
18
+ # Wait a moment for services to fully initialize
19
+ puts "\n2. Waiting for services to initialize..."
20
+ sleep 5
21
+
22
+ # Test Parse Server connection
23
+ puts "\n3. Testing Parse Server connection..."
24
+ if Parse::Test::ServerHelper.setup
25
+ puts "✓ Parse Server connection successful"
26
+
27
+ # Test a basic operation
28
+ puts "\n4. Testing basic Parse operations..."
29
+ begin
30
+ # Check client configuration
31
+ client = Parse::Client.client
32
+ puts " Client server_url: #{client.server_url}"
33
+ puts " Client app_id: #{client.app_id}"
34
+ puts " Client has master_key: #{client.master_key.present?}"
35
+
36
+ # Reset any existing data
37
+ Parse::Test::ServerHelper.reset_database!
38
+
39
+ # Create a test user
40
+ user = Parse::Test::ServerHelper.create_test_user(
41
+ username: 'testuser',
42
+ password: 'testpass',
43
+ email: 'test@example.com'
44
+ )
45
+
46
+ puts "✓ Created test user: #{user.username} (ID: #{user.id})"
47
+
48
+ # Create a test object
49
+ test_obj = Parse::Object.new({'className' => 'TestObject', 'name' => 'Test Item', 'value' => 42})
50
+ test_obj.save
51
+
52
+ puts "✓ Created test object: #{test_obj['name']} (ID: #{test_obj.id})"
53
+
54
+ # Query the object back
55
+ query = Parse::Query.new('TestObject')
56
+ query = query.limit(10) # Use limit() method instead of limit=
57
+ results = query.results
58
+ puts "✓ Retrieved #{results.count} test objects"
59
+
60
+ # Test cloud function
61
+ result = Parse.call_function('hello', name: 'Parse Stack')
62
+ puts "✓ Cloud function result: #{result}"
63
+
64
+ puts "\n✅ All tests passed! Parse Server is working correctly."
65
+
66
+ rescue => e
67
+ puts "✗ Error during testing: #{e.message}"
68
+ puts e.backtrace.first(3) if ENV['DEBUG']
69
+ exit 1
70
+ end
71
+ else
72
+ puts "✗ Parse Server connection failed"
73
+ exit 1
74
+ end
75
+
76
+ puts "\n5. Connection information:"
77
+ puts " Parse Server: http://localhost:1337/parse"
78
+ puts " Parse Dashboard: http://localhost:4040"
79
+ puts " Dashboard login: admin/admin"
80
+
81
+ puts "\nTo stop the containers, run:"
82
+ puts " docker-compose -f docker-compose.test.yml down"