mcp_authorization 0.6.0 → 0.6.2
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 +10 -0
- data/lib/mcp_authorization/rbs_schema_compiler.rb +48 -10
- data/lib/mcp_authorization/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7998105e5a6828b9673a8b97918503842f5eea467ee3ced5c8fd8298c1000f15
|
|
4
|
+
data.tar.gz: 4a130479e78bdd4ec696ee78623ff233331afebbd968c715eb9b87296163c94b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 02ae2178372da419ccbaffe5e7df67bc030e158cfd2b055cfa6682e5cf89d1daa428983c1687479e40de9fc466da01e9f5c2313fa561d5b815a48e884a95b4c0
|
|
7
|
+
data.tar.gz: 14ac5790e1e7f12d36c05331a8193744b2355fd30610416265f7d8f8e0aa730654ff2bdf4bf514d3c3b4d3f677e66916014719fdcda9dfc62b90a7dc4891402a
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to this gem are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.6.2] - 2026-07-01
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **A record field whose type is an inline string-literal union with a single field-level tag was misclassified as a per-member-tagged union.** `compile_tagged_record` routes a field into `compile_tagged_union` (which gates each `|`-separated member individually, e.g. `stage: a @feature(x) | b @feature(y)`) whenever `tagged_union_field?` sees an `@` anywhere in the type string plus more than one `|`-separated part. A plain literal union with one *field-level* tag trailing the whole thing — `logic: "AND" | "OR" @desc(...)` — matches that same heuristic even though the tag applies to the field, not an individual member. Misrouting sends each bare literal (`"AND"`, `"OR"`) through `resolve_type`, which only resolves *named alias references*; each literal fell back to `{type: "object"}`, producing `{type: "object", oneOf: [{type: "object"}, {type: "object"}]}` instead of `{type: "string", enum: ["AND", "OR"]}`. `tagged_union_field?` and `tagged_array_union_inner` now require at least one *non-final* `|`-separated member to carry a tag before treating a field as per-member-gated — every genuine per-member-tagged union in this codebase tags each gated member individually, so a tag trailing only the last member is never sufficient on its own. Field-level tags on inline literal unions now fall through to the normal RBS-library path (`visit_rbs_union`), which already resolved them correctly.
|
|
11
|
+
|
|
12
|
+
## [0.6.1] - 2026-07-01
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **Single-line string-literal-union type aliases (e.g. `type logic = "AND" | "OR"`) lost every member after the first.** Both the `# @rbs type` inline-comment parser and the shared `sig/shared/*.rbs` file parser only scanned *subsequent lines* for `| "value"` continuations, so a union written entirely on one line — the common shape for short enums like `"AND" | "OR"` — resolved to `{type: "string", enum: ["AND"]}` instead of `["AND", "OR"]`. Multi-line unions (`"low"\n| "medium"\n| "high"`) were unaffected. Literal unions written directly inline on a record field (not behind a named alias) were also unaffected, since those go through the RBS-parser union visitor rather than the alias collector.
|
|
16
|
+
|
|
7
17
|
## [0.6.0] - 2026-06-25
|
|
8
18
|
|
|
9
19
|
Per-request work is now scoped to what each MCP method needs, and the
|
|
@@ -1094,10 +1094,19 @@ module McpAuthorization
|
|
|
1094
1094
|
# True when a record field's type is a multi-member union that carries a
|
|
1095
1095
|
# predicate tag — i.e. per-member gating is intended. A `|` at bracket
|
|
1096
1096
|
# depth 0 marks a real union (not one inside a nested generic/record).
|
|
1097
|
+
#
|
|
1098
|
+
# A tag trailing only the *final* member (e.g. a plain literal union
|
|
1099
|
+
# with one field-level `@desc(...)`, as in `"AND" | "OR" @desc(...)`)
|
|
1100
|
+
# is NOT per-member gating — every genuine per-member-tagged union in
|
|
1101
|
+
# this codebase tags each gated member individually
|
|
1102
|
+
# (`a @feature(x) | b @feature(y)`), so at least one *non-final*
|
|
1103
|
+
# member must carry a tag before we route into compile_tagged_union.
|
|
1104
|
+
# Otherwise the field falls through to the normal RBS-library path,
|
|
1105
|
+
# which resolves inline literal unions correctly via visit_rbs_union.
|
|
1097
1106
|
#: (String) -> bool
|
|
1098
1107
|
def tagged_union_field?(type_str)
|
|
1099
1108
|
return false unless type_str.include?("@")
|
|
1100
|
-
|
|
1109
|
+
per_member_tagged_union?(type_str, "|")
|
|
1101
1110
|
end
|
|
1102
1111
|
|
|
1103
1112
|
# If a field type is +Array[<multi-member tagged union>]+, return the inner
|
|
@@ -1111,7 +1120,18 @@ module McpAuthorization
|
|
|
1111
1120
|
return nil unless m
|
|
1112
1121
|
|
|
1113
1122
|
inner = m[1].to_s
|
|
1114
|
-
|
|
1123
|
+
per_member_tagged_union?(inner, "|") ? inner : nil
|
|
1124
|
+
end
|
|
1125
|
+
|
|
1126
|
+
# True when a `|`-separated type expression has more than one member
|
|
1127
|
+
# AND at least one *non-final* member carries a predicate tag. See
|
|
1128
|
+
# tagged_union_field? for why the final member alone doesn't count.
|
|
1129
|
+
#: (String, String) -> bool
|
|
1130
|
+
def per_member_tagged_union?(type_str, delimiter)
|
|
1131
|
+
parts = split_at_depth_zero(type_str, delimiter)
|
|
1132
|
+
return false unless parts.size > 1
|
|
1133
|
+
|
|
1134
|
+
parts[0..-2].any? { |part| part.include?("@") }
|
|
1115
1135
|
end
|
|
1116
1136
|
|
|
1117
1137
|
# Compile a union-style output type (+# @rbs type output = success | admin_detail @requires(:admin)+)
|
|
@@ -1364,7 +1384,7 @@ module McpAuthorization
|
|
|
1364
1384
|
current_name = $1.to_s
|
|
1365
1385
|
current_base = nil
|
|
1366
1386
|
current_body = "{"
|
|
1367
|
-
elsif stripped =~ /\Atype (\w+) = "([^"]
|
|
1387
|
+
elsif stripped =~ /\Atype (\w+) = ("[^"]*"(?:\s*\|\s*"[^"]*")*)/
|
|
1368
1388
|
aliases[$1.to_s] = parse_rbs_string_union($2.to_s, line, content)
|
|
1369
1389
|
elsif current_name
|
|
1370
1390
|
current_body << strip_rbs_comment(stripped)
|
|
@@ -1380,15 +1400,24 @@ module McpAuthorization
|
|
|
1380
1400
|
aliases
|
|
1381
1401
|
end
|
|
1382
1402
|
|
|
1383
|
-
# Parse a
|
|
1403
|
+
# Parse a string literal union from an .rbs file, either written on
|
|
1404
|
+
# one line:
|
|
1405
|
+
# type logic = "AND" | "OR"
|
|
1406
|
+
# or continued across multiple lines:
|
|
1384
1407
|
# type priority = "low"
|
|
1385
1408
|
# | "medium"
|
|
1386
1409
|
# | "high"
|
|
1387
1410
|
#
|
|
1411
|
+
# +first_segment+ is everything captured after the +=+ on the
|
|
1412
|
+
# opening line, which may itself already contain the full
|
|
1413
|
+
# +"a" | "b" | "c"+ union — a single-line union has no continuation
|
|
1414
|
+
# lines, so capturing only its first quoted literal (the prior
|
|
1415
|
+
# behavior) silently dropped every member after the first.
|
|
1416
|
+
#
|
|
1388
1417
|
# @return [Hash] +{type: "string", enum: ["low", "medium", "high"]}+
|
|
1389
1418
|
#: (String, String, String) -> Hash[Symbol, untyped]
|
|
1390
|
-
def parse_rbs_string_union(
|
|
1391
|
-
values = [
|
|
1419
|
+
def parse_rbs_string_union(first_segment, line, content)
|
|
1420
|
+
values = first_segment.scan(/"([^"]*)"/).flatten
|
|
1392
1421
|
content.each_line.drop_while { |l| l != line }.drop(1).each do |next_line|
|
|
1393
1422
|
if next_line =~ /^\s*\|\s*"([^"]+)"/
|
|
1394
1423
|
values << $1.to_s
|
|
@@ -1491,7 +1520,7 @@ module McpAuthorization
|
|
|
1491
1520
|
current_name = nil
|
|
1492
1521
|
current_body = +""
|
|
1493
1522
|
end
|
|
1494
|
-
elsif line =~ /#\s*@rbs type (\w+)\s*=\s*"([^"]
|
|
1523
|
+
elsif line =~ /#\s*@rbs type (\w+)\s*=\s*("[^"]*"(?:\s*\|\s*"[^"]*")*)/
|
|
1495
1524
|
aliases[$1.to_s] = parse_string_union($2.to_s, line, content)
|
|
1496
1525
|
elsif current_name
|
|
1497
1526
|
stripped = strip_rbs_comment(line.strip.sub(/^#\s*/, ""))
|
|
@@ -1961,15 +1990,24 @@ module McpAuthorization
|
|
|
1961
1990
|
str.count("{") == str.count("}")
|
|
1962
1991
|
end
|
|
1963
1992
|
|
|
1964
|
-
# Parse a
|
|
1993
|
+
# Parse a string literal union from handler source comments, either
|
|
1994
|
+
# written on one line:
|
|
1995
|
+
# # @rbs type logic = "AND" | "OR"
|
|
1996
|
+
# or continued across multiple lines:
|
|
1965
1997
|
# # @rbs type priority = "low"
|
|
1966
1998
|
# # | "medium"
|
|
1967
1999
|
# # | "high"
|
|
1968
2000
|
#
|
|
2001
|
+
# +first_segment+ is everything captured after the +=+ on the
|
|
2002
|
+
# opening line, which may itself already contain the full
|
|
2003
|
+
# +"a" | "b" | "c"+ union — a single-line union has no continuation
|
|
2004
|
+
# lines, so capturing only its first quoted literal (the prior
|
|
2005
|
+
# behavior) silently dropped every member after the first.
|
|
2006
|
+
#
|
|
1969
2007
|
# @return [Hash] +{type: "string", enum: ["low", "medium", "high"]}+
|
|
1970
2008
|
#: (String, String, String) -> Hash[Symbol, untyped]
|
|
1971
|
-
def parse_string_union(
|
|
1972
|
-
values = [
|
|
2009
|
+
def parse_string_union(first_segment, line, content)
|
|
2010
|
+
values = first_segment.scan(/"([^"]*)"/).flatten
|
|
1973
2011
|
content.each_line.drop_while { |l| l != line }.drop(1).each do |next_line|
|
|
1974
2012
|
if next_line =~ /^\s*#\s*\|\s*"([^"]+)"/
|
|
1975
2013
|
values << $1.to_s
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mcp_authorization
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- AndyGauge
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|