parse-stack-next 5.5.4 → 5.5.5
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/CHANGELOG.md +8 -6
- data/README.md +26 -13
- data/bin/parse-console +9 -1
- data/docs/TEST_SERVER.md +115 -238
- data/docs/mcp_guide.md +1 -1
- data/docs/mongodb_index_optimization_guide.md +3 -2
- data/docs/usage_guide.md +1 -1
- data/docs/yard-template/default/fulldoc/html/css/common.css +52 -9
- data/docs/yard-template/default/fulldoc/html/css/full_list.css +40 -13
- data/lib/parse/agent/constraint_translator.rb +18 -18
- data/lib/parse/agent/errors.rb +29 -7
- data/lib/parse/agent/metadata_dsl.rb +6 -6
- data/lib/parse/agent/tools.rb +250 -59
- data/lib/parse/agent.rb +42 -30
- data/lib/parse/api/aggregate.rb +3 -3
- data/lib/parse/api/cloud_functions.rb +19 -10
- data/lib/parse/api/objects.rb +8 -8
- data/lib/parse/api/users.rb +9 -9
- data/lib/parse/atlas_search/session.rb +34 -34
- data/lib/parse/atlas_search.rb +243 -110
- data/lib/parse/client/body_builder.rb +10 -10
- data/lib/parse/client/logging.rb +5 -2
- data/lib/parse/client/profiling.rb +5 -2
- data/lib/parse/client/protocol.rb +1 -1
- data/lib/parse/client/url_redaction.rb +94 -0
- data/lib/parse/client.rb +43 -28
- data/lib/parse/embeddings/image_fetch.rb +6 -1
- data/lib/parse/embeddings/voyage.rb +16 -17
- data/lib/parse/live_query/client.rb +7 -7
- data/lib/parse/live_query/subscription.rb +1 -1
- data/lib/parse/lock.rb +1 -1
- data/lib/parse/lock_backend.rb +118 -2
- data/lib/parse/model/acl.rb +24 -24
- data/lib/parse/model/classes/job_schedule.rb +8 -8
- data/lib/parse/model/classes/job_status.rb +9 -9
- data/lib/parse/model/classes/role.rb +49 -49
- data/lib/parse/model/classes/session.rb +2 -2
- data/lib/parse/model/classes/user.rb +66 -66
- data/lib/parse/model/core/builder.rb +7 -7
- data/lib/parse/model/core/create_lock.rb +1 -1
- data/lib/parse/model/core/properties.rb +4 -4
- data/lib/parse/model/file.rb +57 -16
- data/lib/parse/model/model.rb +19 -19
- data/lib/parse/model/object.rb +38 -38
- data/lib/parse/model/pointer.rb +4 -4
- data/lib/parse/model/push.rb +5 -5
- data/lib/parse/mongodb.rb +84 -26
- data/lib/parse/pipeline_security.rb +2 -2
- data/lib/parse/query/constraints.rb +38 -38
- data/lib/parse/query.rb +151 -75
- data/lib/parse/retrieval/reranker/cohere.rb +30 -0
- data/lib/parse/schema.rb +1 -1
- data/lib/parse/stack/version.rb +1 -1
- data/lib/parse/stack.rb +23 -10
- data/lib/parse/two_factor_auth/user_extension.rb +25 -25
- data/lib/parse/webhooks/payload.rb +35 -35
- data/lib/parse/webhooks/registration.rb +2 -2
- data/lib/parse/webhooks/replay_protection.rb +16 -16
- data/lib/parse/webhooks.rb +11 -11
- data/parse-stack-next.gemspec +19 -1
- metadata +2 -38
- data/.bundle/config +0 -5
- data/.env.sample +0 -138
- data/.env.test +0 -10
- data/.github/ISSUE_TEMPLATE/bug_report.yml +0 -105
- data/.github/ISSUE_TEMPLATE/feature_request.yml +0 -67
- data/.github/dependabot.yml +0 -13
- data/.github/workflows/codeql.yml +0 -44
- data/.github/workflows/docs.yml +0 -39
- data/.github/workflows/release.yml +0 -43
- data/.github/workflows/ruby.yml +0 -38
- data/.gitignore +0 -56
- data/.ruby-version +0 -1
- data/.solargraph.yml +0 -22
- data/.vscode/settings.json +0 -3
- data/.yardopts +0 -19
- data/Gemfile +0 -43
- data/Gemfile.lock +0 -198
- data/Makefile +0 -63
- data/Rakefile +0 -825
- data/config/parse-config.json +0 -12
- data/scripts/debug-ips.js +0 -35
- data/scripts/docker/Dockerfile.parse +0 -17
- data/scripts/docker/atlas-init.js +0 -284
- data/scripts/docker/docker-compose.atlas.yml +0 -80
- data/scripts/docker/docker-compose.test.yml +0 -159
- data/scripts/docker/docker-compose.verifyemail.yml +0 -4
- data/scripts/docker/mongo-init.js +0 -21
- data/scripts/docker/preflight.sh +0 -76
- data/scripts/eval_mcp_with_lm_studio.rb +0 -274
- data/scripts/start-parse.sh +0 -154
- data/scripts/start_mcp_server.rb +0 -78
- data/scripts/test_server_connection.rb +0 -82
- data/scripts/vector_prototype/create_vector_index.js +0 -105
- data/scripts/vector_prototype/fetch_embeddings.py +0 -241
- data/scripts/vector_prototype/fixture_manifest.json +0 -9
- data/scripts/vector_prototype/query_prototype.rb +0 -84
- data/scripts/vector_prototype/run.sh +0 -34
|
@@ -438,17 +438,38 @@ input[name=q]::placeholder {
|
|
|
438
438
|
margin-top: 1.4em;
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
-
|
|
441
|
+
/* YARD emits the section letters as bare `<li class="letter">A</li>`
|
|
442
|
+
* (no inner h2), so style the li itself. */
|
|
443
|
+
#listing ul.alpha li.letter {
|
|
442
444
|
color: var(--accent);
|
|
443
|
-
|
|
444
|
-
display: inline-block;
|
|
445
|
-
padding: 0 .8em .2em 0;
|
|
445
|
+
font-weight: 650;
|
|
446
446
|
}
|
|
447
447
|
|
|
448
448
|
#listing ul li a {
|
|
449
449
|
border-bottom: 1px dotted var(--border);
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
+
/* The "(Parse::Constraint)" origin annotations; stock #666 is too dim
|
|
453
|
+
* against the dark surfaces. */
|
|
454
|
+
#listing ul small {
|
|
455
|
+
color: var(--fg-muted);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/* Stock style.css stripes index rows with hardcoded light grays
|
|
459
|
+
* (li.r1 #f0f0f0 / li.r2 #fafafa) that glow white in dark mode. */
|
|
460
|
+
li.r1 { background: var(--bg-soft); }
|
|
461
|
+
li.r2 { background: var(--bg); }
|
|
462
|
+
|
|
463
|
+
/* File Listing pills at the top of _index.html. */
|
|
464
|
+
.index_inline_list li,
|
|
465
|
+
.index_inline_list li.r1,
|
|
466
|
+
.index_inline_list li.r2 {
|
|
467
|
+
background: var(--bg-soft);
|
|
468
|
+
border: 1px solid var(--border);
|
|
469
|
+
border-radius: var(--radius-sm);
|
|
470
|
+
margin-right: 6px;
|
|
471
|
+
}
|
|
472
|
+
|
|
452
473
|
/* -------- inheritance / mixin lists -------- */
|
|
453
474
|
|
|
454
475
|
dl.box {
|
|
@@ -892,9 +913,13 @@ h2.h2_sum,
|
|
|
892
913
|
|
|
893
914
|
/* The collapse arrows in the class-tree iframe are a base64 PNG
|
|
894
915
|
* sprite of dark glyphs on a transparent background — invisible
|
|
895
|
-
* against our dark surface. CSS filter inverts them in place.
|
|
896
|
-
|
|
897
|
-
|
|
916
|
+
* against our dark surface. CSS filter inverts them in place.
|
|
917
|
+
* Dark mode only: in light mode the stock dark glyphs are correct,
|
|
918
|
+
* and inverting them would wash them out against the light rail. */
|
|
919
|
+
@media (prefers-color-scheme: dark) {
|
|
920
|
+
#full_list li a.toggle {
|
|
921
|
+
filter: invert(0.85) hue-rotate(180deg) brightness(1.6);
|
|
922
|
+
}
|
|
898
923
|
}
|
|
899
924
|
|
|
900
925
|
/* ============================================================
|
|
@@ -928,11 +953,29 @@ h2.h2_sum,
|
|
|
928
953
|
padding: 1px 6px !important;
|
|
929
954
|
}
|
|
930
955
|
|
|
931
|
-
/* But never on the code blocks themselves (the pre.code descendants).
|
|
956
|
+
/* But never on the code blocks themselves (the pre.code descendants).
|
|
957
|
+
* The scoped variants are required, not belt-and-braces: file pages
|
|
958
|
+
* (README + guides) compile fenced blocks to <pre><code class="ruby">,
|
|
959
|
+
* and with both rules !important the positive `#filecontents code`
|
|
960
|
+
* (0,1,0,1) would otherwise beat a bare `pre code` (0,0,0,2) — the
|
|
961
|
+
* inline-level <code> then draws its border on every wrapped line
|
|
962
|
+
* fragment, streaking the whole block. */
|
|
932
963
|
pre code,
|
|
933
964
|
pre tt,
|
|
934
965
|
pre.code code,
|
|
935
|
-
pre.code tt
|
|
966
|
+
pre.code tt,
|
|
967
|
+
.docstring pre code,
|
|
968
|
+
.docstring pre tt,
|
|
969
|
+
.tags pre code,
|
|
970
|
+
.tags pre tt,
|
|
971
|
+
.summary pre code,
|
|
972
|
+
.summary pre tt,
|
|
973
|
+
#filecontents pre code,
|
|
974
|
+
#filecontents pre tt,
|
|
975
|
+
.discussion pre code,
|
|
976
|
+
.discussion pre tt,
|
|
977
|
+
.method_details pre code,
|
|
978
|
+
.method_details pre tt {
|
|
936
979
|
background: transparent !important;
|
|
937
980
|
border: 0 !important;
|
|
938
981
|
padding: 0 !important;
|
|
@@ -209,19 +209,43 @@ a:visited {
|
|
|
209
209
|
* parse-stack-next overlay (sidebar / class-list iframe)
|
|
210
210
|
* ============================================================ */
|
|
211
211
|
|
|
212
|
+
/* Scheme-aware palette. Light is the default; the dark block below
|
|
213
|
+
* mirrors common.css's prefers-color-scheme handling. Keeping the
|
|
214
|
+
* overlay unconditional (as it originally was) made the rail
|
|
215
|
+
* unreadable in light mode: common.css paints #full_list with the
|
|
216
|
+
* light surface while these rules kept the near-white dark-mode
|
|
217
|
+
* text (its `#full_list li .item a` selector out-specifies
|
|
218
|
+
* common.css's `#full_list li a` regardless of load order). */
|
|
212
219
|
:root {
|
|
213
|
-
--ps-bg: #
|
|
214
|
-
--ps-bg-soft: #
|
|
215
|
-
--ps-bg-hover: #
|
|
216
|
-
--ps-fg: #
|
|
217
|
-
--ps-fg-muted: #
|
|
218
|
-
--ps-accent: #
|
|
219
|
-
--ps-link: #
|
|
220
|
-
--ps-border: #
|
|
220
|
+
--ps-bg: #FFFFFF;
|
|
221
|
+
--ps-bg-soft: #F8F9FA;
|
|
222
|
+
--ps-bg-hover: #E8EAED;
|
|
223
|
+
--ps-fg: #1F2328;
|
|
224
|
+
--ps-fg-muted: #6B7280;
|
|
225
|
+
--ps-accent: #B22234;
|
|
226
|
+
--ps-link: #169CEE;
|
|
227
|
+
--ps-border: #E5E7EB;
|
|
228
|
+
--ps-scroll-thumb: #C7CBD1;
|
|
229
|
+
--ps-header-shadow: 0 1px 3px rgba(0, 0, 0, .08);
|
|
221
230
|
--ps-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter",
|
|
222
231
|
Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
223
232
|
}
|
|
224
233
|
|
|
234
|
+
@media (prefers-color-scheme: dark) {
|
|
235
|
+
:root {
|
|
236
|
+
--ps-bg: #1E1F22;
|
|
237
|
+
--ps-bg-soft: #2A2C30;
|
|
238
|
+
--ps-bg-hover: #34373C;
|
|
239
|
+
--ps-fg: #E6E8EB;
|
|
240
|
+
--ps-fg-muted: #9CA3AF;
|
|
241
|
+
--ps-accent: #E63946;
|
|
242
|
+
--ps-link: #4FB8F4;
|
|
243
|
+
--ps-border: #3A3D42;
|
|
244
|
+
--ps-scroll-thumb: #4a4d52;
|
|
245
|
+
--ps-header-shadow: 0 1px 3px rgba(0, 0, 0, .3);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
225
249
|
body {
|
|
226
250
|
background: var(--ps-bg) !important;
|
|
227
251
|
color: var(--ps-fg);
|
|
@@ -238,7 +262,7 @@ h1 {
|
|
|
238
262
|
.fixed_header {
|
|
239
263
|
background: var(--ps-bg-soft) !important;
|
|
240
264
|
border-bottom: 1px solid var(--ps-border);
|
|
241
|
-
box-shadow:
|
|
265
|
+
box-shadow: var(--ps-header-shadow);
|
|
242
266
|
}
|
|
243
267
|
|
|
244
268
|
#search input,
|
|
@@ -299,7 +323,7 @@ h1 {
|
|
|
299
323
|
|
|
300
324
|
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
301
325
|
::-webkit-scrollbar-track { background: var(--ps-bg); }
|
|
302
|
-
::-webkit-scrollbar-thumb { background:
|
|
326
|
+
::-webkit-scrollbar-thumb { background: var(--ps-scroll-thumb); border-radius: 4px; }
|
|
303
327
|
::-webkit-scrollbar-thumb:hover { background: var(--ps-link); }
|
|
304
328
|
|
|
305
329
|
/* ============================================================
|
|
@@ -381,7 +405,10 @@ body > #content {
|
|
|
381
405
|
}
|
|
382
406
|
|
|
383
407
|
/* The collapse toggle PNG is a dark-on-transparent sprite; invert it
|
|
384
|
-
* so the arrow shows on dark backgrounds.
|
|
385
|
-
|
|
386
|
-
|
|
408
|
+
* so the arrow shows on dark backgrounds. Dark mode only — in light
|
|
409
|
+
* mode the stock dark glyphs are already correct. */
|
|
410
|
+
@media (prefers-color-scheme: dark) {
|
|
411
|
+
#full_list li a.toggle {
|
|
412
|
+
filter: invert(0.85) hue-rotate(180deg) brightness(1.6);
|
|
413
|
+
}
|
|
387
414
|
}
|
|
@@ -72,9 +72,9 @@ module Parse
|
|
|
72
72
|
].freeze
|
|
73
73
|
|
|
74
74
|
# Operators whose value carries an inner sub-query of the shape
|
|
75
|
-
#
|
|
75
|
+
# `{className:, where:, key:}`. Each must be validated through
|
|
76
76
|
# {Tools.assert_class_accessible!} so the LLM cannot reach into a
|
|
77
|
-
# hidden class via the sub-query, and the inner
|
|
77
|
+
# hidden class via the sub-query, and the inner `where` must be
|
|
78
78
|
# recursively re-translated so blocked operators inside it are
|
|
79
79
|
# also caught.
|
|
80
80
|
CROSS_CLASS_OPERATORS = %w[
|
|
@@ -260,11 +260,11 @@ module Parse
|
|
|
260
260
|
end
|
|
261
261
|
|
|
262
262
|
# Translate the value of a cross-class operator
|
|
263
|
-
# (
|
|
264
|
-
# carries an embedded
|
|
265
|
-
# the active accessibility policy, and an embedded
|
|
263
|
+
# (`$inQuery`/`$notInQuery`/`$select`/`$dontSelect`). The value
|
|
264
|
+
# carries an embedded `className` that must be validated against
|
|
265
|
+
# the active accessibility policy, and an embedded `where` that
|
|
266
266
|
# must be recursively translated so blocked operators (e.g.
|
|
267
|
-
#
|
|
267
|
+
# `$where` nested inside) cannot smuggle through.
|
|
268
268
|
def translate_cross_class_value(op, val, depth:, agent: nil)
|
|
269
269
|
return val unless val.is_a?(Hash)
|
|
270
270
|
val = val.transform_keys(&:to_s)
|
|
@@ -321,20 +321,20 @@ module Parse
|
|
|
321
321
|
translate_value(val, depth: depth, agent: agent)
|
|
322
322
|
end
|
|
323
323
|
|
|
324
|
-
# Validate the owning-object class named by a
|
|
324
|
+
# Validate the owning-object class named by a `$relatedTo` constraint.
|
|
325
325
|
#
|
|
326
|
-
#
|
|
327
|
-
# Unlike
|
|
326
|
+
# `$relatedTo` has the shape `{ object: <Pointer>, key: <relation field> }`.
|
|
327
|
+
# Unlike `$inQuery` / `$select` it carries no `className` / inner `where`,
|
|
328
328
|
# so it is NOT a {CROSS_CLASS_OPERATORS} entry — but it DOES reach across
|
|
329
329
|
# to a second class: the owning object whose relation is being read. Left
|
|
330
330
|
# unvalidated, an agent narrowed to one class (or with a class globally
|
|
331
|
-
#
|
|
332
|
-
# class via the
|
|
333
|
-
# GHSA-wmwx-jr2p-4j4r, where Parse Server's own
|
|
331
|
+
# `agent_hidden`) could still name a relation anchored on an off-allowlist
|
|
332
|
+
# class via the `object` pointer. That is the SDK-surface analog of
|
|
333
|
+
# GHSA-wmwx-jr2p-4j4r, where Parse Server's own `$relatedTo` bypassed the
|
|
334
334
|
# owning object's ACL. Run the owning class through the same accessibility
|
|
335
335
|
# policy as every other cross-class hop, then translate the value normally.
|
|
336
336
|
#
|
|
337
|
-
# Fails closed when the owning class cannot be resolved from
|
|
337
|
+
# Fails closed when the owning class cannot be resolved from `object`: an
|
|
338
338
|
# unresolvable pointer is exactly the shape that would otherwise slip the
|
|
339
339
|
# check, so refuse the constraint rather than skip it.
|
|
340
340
|
def translate_related_to_value(val, depth:, agent: nil)
|
|
@@ -351,10 +351,10 @@ module Parse
|
|
|
351
351
|
translate_value(val, depth: depth, agent: agent)
|
|
352
352
|
end
|
|
353
353
|
|
|
354
|
-
# Extract the Parse class name of a
|
|
355
|
-
# object from its
|
|
356
|
-
# pointer/relation hash (
|
|
357
|
-
# symbol keys), or a storage-form string (
|
|
354
|
+
# Extract the Parse class name of a `$relatedTo` constraint's owning
|
|
355
|
+
# object from its `object` slot, which may be a Parse::Pointer, a Parse
|
|
356
|
+
# pointer/relation hash (`{__type:, className:, objectId:}`, string or
|
|
357
|
+
# symbol keys), or a storage-form string (`"ClassName$objectId"`).
|
|
358
358
|
# Returns nil when no class can be resolved so the caller can fail closed.
|
|
359
359
|
def related_to_owning_class(val)
|
|
360
360
|
return nil unless val.is_a?(Hash)
|
|
@@ -371,7 +371,7 @@ module Parse
|
|
|
371
371
|
end
|
|
372
372
|
|
|
373
373
|
# Hook into the agent-side accessibility check when the agent
|
|
374
|
-
# module is loaded; in pure-unit contexts where
|
|
374
|
+
# module is loaded; in pure-unit contexts where `Parse::Agent::Tools`
|
|
375
375
|
# has not been loaded, default to a no-op rather than raising —
|
|
376
376
|
# the strict check is enforced wherever the agent dispatches.
|
|
377
377
|
def assert_embedded_class_accessible!(op, class_name, agent: nil)
|
data/lib/parse/agent/errors.rb
CHANGED
|
@@ -121,22 +121,22 @@ module Parse
|
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
-
# Raised inside the
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
124
|
+
# Raised inside the `call_method` tool when the resolved
|
|
125
|
+
# `ClassName.method_name` is excluded by the agent instance's
|
|
126
|
+
# `methods:` filter. The execute() rescue maps this to a
|
|
127
|
+
# `:tool_filtered` error_code so consumers can distinguish "the
|
|
128
128
|
# filter excluded this method" from "this method isn't declared
|
|
129
129
|
# agent-callable" (a Parse::Error) or "the tier doesn't allow it"
|
|
130
|
-
# (a
|
|
130
|
+
# (a `:permission_denied`).
|
|
131
131
|
class MethodFiltered < AgentError; end
|
|
132
132
|
|
|
133
133
|
# Raised at semantic-search dispatch time when at least one class in
|
|
134
|
-
# the model registry declares
|
|
134
|
+
# the model registry declares `agent_tenant_scope` but the class
|
|
135
135
|
# being searched does not. In a tenant-aware deployment an
|
|
136
136
|
# un-scoped searchable surface would let an agent retrieve across
|
|
137
137
|
# tenant boundaries, so the gate is a hard refusal, not a warning.
|
|
138
138
|
# Enforced at dispatch (when all classes are loaded) rather than at
|
|
139
|
-
#
|
|
139
|
+
# `agent_searchable` declaration time so class-load order can't
|
|
140
140
|
# produce a false negative.
|
|
141
141
|
class MissingTenantScope < AgentError; end
|
|
142
142
|
|
|
@@ -145,5 +145,27 @@ module Parse
|
|
|
145
145
|
# `:refuse`. A SecurityError subclass so it routes through execute's
|
|
146
146
|
# security rescue and is never swallowed.
|
|
147
147
|
class PromptInjectionDetected < SecurityError; end
|
|
148
|
+
|
|
149
|
+
# Raised by Parse::Agent::Tools.invoke when a tool name is recognized
|
|
150
|
+
# (advertised in a permission tier / passes the gates) but has no
|
|
151
|
+
# handler in this SDK version. The built-in raw-CRUD write/admin tools
|
|
152
|
+
# (`create_object`, `update_object`, `delete_object`, `create_class`,
|
|
153
|
+
# `delete_class`) are declared in the write/admin tiers but ship
|
|
154
|
+
# without an implementation — invoking one previously raised a bare
|
|
155
|
+
# `NoMethodError` that collapsed to an opaque "internal error" on the
|
|
156
|
+
# wire. This turns that into a clear, typed refusal so the caller
|
|
157
|
+
# learns the tool is not implemented rather than that the SDK broke.
|
|
158
|
+
class NotImplemented < AgentError
|
|
159
|
+
attr_reader :tool_name
|
|
160
|
+
|
|
161
|
+
def initialize(tool_name, message = nil)
|
|
162
|
+
@tool_name = tool_name.to_s
|
|
163
|
+
super(message || "Tool '#{@tool_name}' is recognized but not implemented in this " \
|
|
164
|
+
"SDK version. The built-in raw create/update/delete tools are not " \
|
|
165
|
+
"available; expose the operation as an application method via " \
|
|
166
|
+
"`agent_method` + `call_method`, or register a handler with " \
|
|
167
|
+
"`Parse::Agent::Tools.register`.")
|
|
168
|
+
end
|
|
169
|
+
end
|
|
148
170
|
end
|
|
149
171
|
end
|
|
@@ -450,15 +450,15 @@ module Parse
|
|
|
450
450
|
# preview-only execution. When false (default), passing dry_run: true in
|
|
451
451
|
# arguments is refused at dispatch time with :invalid_argument.
|
|
452
452
|
# @param permitted_keys [Array<Symbol,String>, nil] when provided,
|
|
453
|
-
#
|
|
453
|
+
# `call_method` refuses any `arguments` key not in this list.
|
|
454
454
|
# Without this, an LLM (or a prompt-injection payload) can
|
|
455
|
-
# pass arbitrary keys through a method that splats with
|
|
456
|
-
# reaching protected columns like
|
|
457
|
-
# Highly recommended on any
|
|
455
|
+
# pass arbitrary keys through a method that splats with `**`,
|
|
456
|
+
# reaching protected columns like `_hashed_password` or `ACL`.
|
|
457
|
+
# Highly recommended on any `agent_write`/`agent_admin` method
|
|
458
458
|
# that takes a kwargs splat.
|
|
459
459
|
# @param parameters [Hash, nil] when provided, a JSON Schema (as a
|
|
460
|
-
# Ruby Hash) describing the
|
|
461
|
-
#
|
|
460
|
+
# Ruby Hash) describing the `arguments` object. Surfaced in
|
|
461
|
+
# `tools/list` so the LLM submits properly-shaped inputs and
|
|
462
462
|
# stricter MCP clients can validate before dispatch.
|
|
463
463
|
# @return [Hash] the method metadata
|
|
464
464
|
def agent_method(method_name, description = nil, permission: :readonly,
|