Skip to content

Commas inside @desc(...) truncate type parsing in records and call signatures #15

@AndyGauge

Description

@AndyGauge

Summary

A comma anywhere inside an @desc(...) annotation truncates type parsing. The capture used for the type expression ([^,}]+ in compile_tagged_record / parse_record_type, and String#split(",") in parse_call_params) stops at the first comma regardless of whether that comma is part of natural English in the description text.

The result: naturally-written descriptions silently corrupt the generated JSON Schema.

Affected versions

Observed against mcp_authorization 0.3.0. The regexes look the same on main.

Reproduction

Minimal handler:

class Handlers::CreateBook
  include McpAuthorization::DSL

  def description
    "Create a book"
  end

  # @rbs import error

  # @rbs type book_input = {
  #   title: String @desc(Title displayed to readers),
  #   page_count: Integer? @desc(Total pages, including front matter),
  #   genre: String? @desc(Optional, defaults to general fiction)
  # }

  # @rbs type success = { success: true, id: Integer }
  # @rbs type output = success | error

  #: (book: book_input) -> output
  def call(book:)
    { success: true, id: 1 }
  end
end

When this handler is wrapped by an McpAuthorization::Tool subclass and surfaced via tools/list, the inputSchema for book is:

{
  "type": "object",
  "properties": {
    "title": { "type": "string", "description": "Title displayed to readers" }
  },
  "required": ["title"]
}

page_count and genre are missing entirely. The commas inside their @desc(...) text caused [^,}]+ to stop mid-type, and the trailing fragments (including front matter), defaults to general fiction)) no longer matched the next \w+: pattern, so those fields were dropped.

Where it happens

In lib/mcp_authorization/rbs_schema_compiler.rb:

  1. compile_tagged_record (~line 654):

    inner.scan(/(\w+\??)\s*:\s*([^,}]+)/) do |match|
  2. parse_record_type (~line 1052):

    inner.scan(/(\w+):\s*([^,}]+)/) do |match|
  3. parse_call_params (~line 1002):

    $1.to_s.split(",").each do |param|

    A comma inside @desc(...) on a call-signature parameter splits the parameter into pieces; both halves then fail the \A(\?)?([\w]+):\s*(.+)\z per-param regex and get skipped.

Expected behavior

Commas inside @desc(...) (and other @tag(...) parens) should be treated as part of the tag's payload, not as field separators. Authors should be able to write natural English in descriptions without worrying about delimiter collisions.

Workaround

Strip all commas from @desc(...) text — replace with em-dashes, semicolons, or rephrase. Tedious for anything written conversationally.

Suggested fix direction

A couple of approaches that would work:

  • Strip balanced-paren tags first. Before splitting the inner body by field, walk the string with a small bracket-aware scanner that removes @\w+\(...\) segments (matching nested parens) and stashes them for later extract_tags consumption. Then the field-splitter only sees bare key: type pairs.
  • Or switch the field-splitter from regex to a small state machine that tracks paren depth and splits on commas only when depth is zero.

Either keeps backward compatibility with simple cases and unblocks natural descriptions. Happy to send a PR if a maintainer can confirm which direction they'd prefer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions