Skip to content

Resource discovery DX: did-you-mean, relationship targets, session-no…#3

Merged
Azdaroth merged 3 commits into
masterfrom
mcp-resource-discovery-dx
Jul 3, 2026
Merged

Resource discovery DX: did-you-mean, relationship targets, session-no…#3
Azdaroth merged 3 commits into
masterfrom
mcp-resource-discovery-dx

Conversation

@Azdaroth

@Azdaroth Azdaroth commented Jul 3, 2026

Copy link
Copy Markdown
Member

…t-found log

Closes three gaps an MCP agent hit when it had to guess a related resource's name instead of discovering it:

  • Registry#fetch now raises a "did you mean" message for an unknown resource: closest registered name(s) via stdlib DidYouMean::SpellChecker (dependency-free edit-distance fallback) + the full list when the catalog is short. Flows unchanged to the caller as a clean InvalidParams tool error via resolve_descriptor.
  • resource_schema now names each relationship's target_resource (callable via list/get) plus a best-effort target_name_attribute hint, resolved through the registry. So scheduled_notifications.notification is discoverably the notifications resource. Additive/backward-compatible: both fields are omitted when unresolved (e.g. polymorphic links).
  • The transport controller logs a WARN on session-not-found: greppable, id/token-free, records only whether a session-id header was present, so a non-shared cache_store misconfiguration shows in a satellite's own logs. Logger defaults to Rails.logger, overridable via mcp_logger.

Bumps to 0.3.0. Full suite + rubocop + brakeman green.

…t-found log

Closes three gaps an MCP agent hit when it had to guess a related
resource's name instead of discovering it:

- Registry#fetch now raises a "did you mean" message for an unknown
  resource: closest registered name(s) via stdlib DidYouMean::SpellChecker
  (dependency-free edit-distance fallback) + the full list when the catalog
  is short. Flows unchanged to the caller as a clean InvalidParams tool
  error via resolve_descriptor.
- resource_schema now names each relationship's target_resource (callable
  via list/get) plus a best-effort target_name_attribute hint, resolved
  through the registry. So scheduled_notifications.notification is
  discoverably the `notifications` resource. Additive/backward-compatible:
  both fields are omitted when unresolved (e.g. polymorphic links).
- The transport controller logs a WARN on session-not-found: greppable,
  id/token-free, records only whether a session-id header was present, so
  a non-shared cache_store misconfiguration shows in a satellite's own
  logs. Logger defaults to Rails.logger, overridable via mcp_logger.

Bumps to 0.3.0. Full suite + rubocop + brakeman green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Azdaroth Azdaroth self-assigned this Jul 3, 2026
# Logger for transport-level diagnostics. Defaults to Rails.logger when running
# inside Rails (nil outside it); overridable so a host can inject its own.
def mcp_logger
return Rails.logger if defined?(Rails) && Rails.respond_to?(:logger)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no way Rails won't respond to logger, hat's excessive. Also don't fallback to nil, instead fallback maybe to STDOUT

# a header-missing client bug is distinguishable from a cache misconfiguration.
def mcp_log_session_not_found
logger = mcp_logger
return unless logger

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woith a better fallback, that will be excessive

Comment thread lib/mcp_toolkit/registry.rb Outdated
# nearest registered name(s) for a near-miss and (b) — when the catalog is short —
# lists them all, so a caller (typically an MCP agent that guessed a name) can
# self-correct without another round-trip to the `resources` tool.
def unknown_resource_message(name)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move all of these related methods to a dedicated class, no need to clutter registry. Just delegate. Don't add methods comments, just a top-level class comment.

Comment thread lib/mcp_toolkit/resource_schema.rb Outdated
kind: association.type.to_s,
polymorphic: association.polymorphic || false,
target_resource: target&.name,
target_name_attribute: name_attribute_for(target)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

target attribute name is excessive, let's get rid of this concept here

…, drop target_name_attribute

- Move did-you-mean/levenshtein logic out of Registry into a dedicated
  McpToolkit::UnknownResourceMessage (Registry just delegates).
- mcp_logger: drop the redundant respond_to?(:logger) check; fall back to a
  $stdout Logger instead of nil, so mcp_log_session_not_found needs no guard.
- Remove the target_name_attribute concept from resource_schema (keep
  target_resource): schema, tool description, CHANGELOG, and specs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# and (b), when the catalog is short, lists them all, so a caller (typically an MCP
# agent that guessed a name) can self-correct without another round-trip to the
# `resources` tool.
class McpToolkit::UnknownResourceMessage

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some basic test coverage would be useful

Addresses PR #3 review: basic tests for the extracted class — empty
registry, near-miss suggestion, short-catalog listing, large-catalog
no-op, and the dependency-free edit-distance fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Azdaroth Azdaroth merged commit 23726d4 into master Jul 3, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant