Previously, the `icons` parameter was always included in responses (as an empty array)
even when not configured. This differs from the response representation in MCP Ruby SDK 0.4.0.
Since `icons` is optional, it should be omitted from responses when unused to reduce
unnecessary context window usage.
https://modelcontextprotocol.io/specification/2025-11-25/schema#implementation
This fix applies to `Tool#to_h`, `Prompt#to_h`, `Resource#to_h`, `ResourceTemplate#to_h`,
and `Server#server_info`.
It doesn't seem critical, but if there are no other issues, it would be good to cut 0.5.1 bugfix release soon.
## Summary
Replace regex patterns with lookahead/lookbehind assertions to prevent
catastrophic backtracking on Ruby 3.1 and earlier.
This issue only affects Ruby 3.1 and earlier. Since it does not occur on Ruby 3.2+,
it is not an issue in the current stable release (0.4.0), which does not include #206.
## Regression Testing
Ensure the regex patterns complete within 1 second for a 50,000-character input
that would otherwise trigger superlinear backtracking.
- Set Accept header to request JSON or SSE responses
- Validate response Content-Type and raise error for non-JSON responses
- Add Accept header to test fixtures
- Use explicit hash syntax for Ruby 2.7 compatibility
## Summary
Unlike applications, libraries provide value by remaining usable for a broad range of users,
even when that means supporting older Ruby versions beyond Ruby's own EOL timeline.
I maintain RuboCop, a Ruby linter. Because linters need to work across the ecosystem,
they often need to keep compatibility with older Ruby versions to some extent.
The trade-off is that the MCP Ruby SDK must remain compatible with Ruby 2.7.
However, given the ecosystem impact, supporting Ruby 2.7 is still worthwhile.
For example, bundled gems such as `csv` (Ruby >= 2.5), `bigdecimal` (Ruby >= 2.5), `json` (Ruby >= 2.7),
and `language_server-protocol` (Ruby >= 2.5) tend to keep compatibility with older Ruby versions.
Unlike dropping support, which imposes restrictions on users,
this change broadens compatibility and does not introduce breaking changes.
## Development Note
`rubocop-shopify` (2.18) supports Ruby 3.1+, and this PR does not change that.
Since RuboCop can configure the runtime Ruby version and the target Ruby version for analysis independently,
CI continues to run RuboCop on Ruby 4.0, while analyzing code with `TargetRubyVersion: 2.7`.
When setting `TargetRubyVersion: 2.7`, several RuboCop offenses were resolved.
Additionally, Sorbet-related libraries support Ruby 3.0+. Since these are not dependencies of the MCP Ruby SDK itself,
they are versioned and tested in the development Gemfile.
Claude Code sometimes only shows `Error: MCP error -32603: Internal error`
when an unexpected exception occurs during a tool call.
This change broadens the exception handling scope so the server can surface
the exception message more often, making failures easier to diagnose.
This raises an exception when duplicate tool names are registered,
instead of silently overwriting tools. Tool names are required to be
unique within a server, so this behavior aligns with the MCP specification.
> Tool names SHOULD be unique within a server.
https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-names
Validation for tool names could be made more strictly spec-compliant in a separate effort.
Fixes#197
This PR adds support for the optional `websiteUrl` parameter in `serverInfo`.
This parameter has been supported since the 2025-11-25 MCP specification.
This PR also implements part of https://github.com/modelcontextprotocol/ruby-sdk/issues/127.
This refactoring extracts common schema validation logic into a base Schema
class, eliminating ~100 lines of duplicate code between InputSchema and
OutputSchema. It also adds support for the JSON Schema `additionalProperties`
keyword to allow fine-grained control over extra properties in tool arguments.
Previously `additionalParameters` could be set only on OutputSchema.
Key changes:
- Created MCP::Tool::Schema base class consolidating:
- Schema validation against JSON Schema Draft 4 metaschema
- Deep key transformation (string to symbol conversion)
- $ref disallowance checking
- Common validation methods
- Refactored InputSchema to inherit from Schema:
- Simplified constructor to accept full schema hash
- Changed required fields storage from symbols to strings
- Removed ~40 lines of duplicated validation logic
- Refactored OutputSchema to inherit from Schema:
- Removed ~54 lines of duplicated validation logic
- Now shares all common functionality with InputSchema
- Updated Tool.input_schema setter:
- Simplified to pass full hash directly to InputSchema
- Added additionalProperties support:
- By default, tools now allow additional properties (JSON Schema default)
- Can explicitly set `additionalProperties: false` to disallow extras
- Added comprehensive test coverage for both behaviors
When starting an MCP server in Claude Code with `protocol_version` set to `DRAFT-2025-v3`,
it fails with the following error:
```console
2025-12-04T02:58:01.548Z [DEBUG] MCP server "example": Connection failed after 2093ms: Server's protocol version is not supported: DRAFT-2025-v3
2025-12-04T02:58:01.548Z [ERROR] MCP server "example" Connection failed: Server's protocol version is not supported: DRAFT-2025-v3
```
This PR changes the default behavior so that if `protocol_version` is not specified,
it uses the latest stable protocol version instead of the draft version.
This PR also updates the README to note that the latest stable protocol version includes
new features from the draft version.
Consolidates four identical constructions of error `Tool::Response` in
`handle_call_tool_request` into a single private `error_tool_response` method,
reducing duplication and improving maintainability. No behavior change.
For example, the `title` introduced in #109 is not supported in the latest stable protocol version "2025-06-18":
https://modelcontextprotocol.io/specification/2025-06-18
It is supported in the Draft of the next version:
https://modelcontextprotocol.io/specification/draft
In other words, the fact that the Ruby SDK treats "2025-06-18" as the default protocol version while `title` is
only supported in the Draft results in an inconsistency.
Because of this, a new version `"Draft"` has been added to the list of supported versions.
Its value follows the schema below and is defined as `DRAFT-2025-v3`:
14ec41c/schema/draft/schema.ts (L15)
It was also considered to continue using the latest stable version "2025-06-18" as the Ruby SDK default instead of the Draft.
However, in that case, users would need to explicitly specify `MCP::Server.new(protocol_version: 'DRAFT-2025-v3')`,
which adds extra burden. In this PR, Draft is made the default in order to provide the following benefits:
- Users are likely to prefer using the Draft version over the latest stable version
- No need to specify the `protocol_version` option when initializing `MCP::Server`
- Users generally do not need to know the internal development value `DRAFT-2025-v3`
The primary case for specifying `protocol_version` explicitly is when strict adherence to a particular version is required.
However, it seems more practical to prioritize use cases where backward-compatible features, including those from the Draft,
are desirable. This is the rationale behind making the Draft version the default.
In addition, making the Draft version the default protocol version preserves backward compatibility for `MCP::Server`.
NOTE: `DRAFT-2025-v3` is a development code, so its value cannot be set externally.
It is applied either when `protocol_version` is omitted or when `nil` is set explicitly,
such as with `MCP::Configuration.new(protocol_version: nil)`.
This prevents users from creating implementations that depend on development codes like `DRAFT-2025-v3`.
- Create failing tests for `stateless` mode
- Implement `stateless` mode by turning of SSE in Streamable HTTP,
making the interaction a standard HTTP Req/Resp
Tool execution errors now return successful responses with isError: true
instead of JSON-RPC protocol errors, per the MCP specification.
This allows clients to distinguish between protocol-level errors and
tool execution errors, providing better error context.
Fixes#159
Follow-up to https://github.com/modelcontextprotocol/ruby-sdk/pull/147.
According to the specification schema, `content` is not optional and therefore cannot be omitted:
```typescript
interface CallToolResult {
_meta?: { [key: string]: unknown };
content: ContentBlock[];
isError?: boolean;
structuredContent?: { [key: string]: unknown };
[key: string]: unknown;
}
```
https://modelcontextprotocol.io/specification/2025-06-18/schema#calltoolresult
Instead of `nil`, an empty array is set as the default value.
There may be a better value for `content`, but at the very least it should not be missing.
* Add support for _meta field on tool schema
* use `meta` naming for `_meta` field on tool schema
* apply suggestions to fix `_meta` tests
Co-authored-by: Ateş Göral <ates@magnetiq.com>
* Update lib/mcp/tool.rb
Co-authored-by: Ateş Göral <ates@magnetiq.com>
* Update lib/mcp/tool.rb
Co-authored-by: Ateş Göral <ates@magnetiq.com>
---------
Co-authored-by: Topher Bullock <topherbullock@users.noreply.github.com>
Co-authored-by: Ateş Göral <ates@magnetiq.com>
I noticed through the existing tests that `MCP::Configuration#protocol_version=`
allows setting an arbitrary protocol version.
Since the official MCP Ruby SDK is an implementation of the MCP specification,
it should not support arbitrary protocol versions. This change adds validation
to the setter, consistent with `Configuration#initialize`.
The same validation has also been added to `MCP::Configuration#validate_tool_call_arguments=`.
There are some unused block variables. This change removes block parameters
that consist solely of unused block parameters.
Where `Hash#values` suffices instead of ignoring unused variables,
replace such code with `Hash#values`.