claude-agent-sdk 0.7.1 → 0.7.3
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 +25 -0
- data/README.md +33 -2
- data/lib/claude_agent_sdk/message_parser.rb +5 -3
- data/lib/claude_agent_sdk/sdk_mcp_server.rb +31 -20
- data/lib/claude_agent_sdk/types.rb +11 -0
- data/lib/claude_agent_sdk/version.rb +1 -1
- data/lib/claude_agent_sdk.rb +2 -2
- 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: d17d03fa867e2779de155d6fba8a86fbc9edbc5662157ca3e21606016c81fb17
|
|
4
|
+
data.tar.gz: 949787994cd58eb5be72b8834a519a704697c61ecbfff9ca1d6f4e1fe921b18f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a99fdb7b2dcab7e0286763abfb1f6d0277423a7ef2198a040ef8c76a8959907009ebc21f6523c35ecff1409a66fd8c636648d1f594aaa481119ed3598eb1b06e
|
|
7
|
+
data.tar.gz: 8e3e7e26487dcfe993534d3d1b7f695ba5009ad18e9d173c9e1e0f1353ec54565905f3e74e81a39eb5789aa2df008536a2a5a191095e7d8604f36abd27bb6583
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.7.3] - 2026-02-26
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **String-keyed JSON schema crash:** Libraries like [RubyLLM](https://github.com/crmne/ruby_llm) that deep-stringify schema keys (e.g., `{ 'type' => 'object', 'properties' => { ... } }`) were misidentified as simple type-mapping schemas, causing each top-level key to be treated as a parameter name instead of passing the schema through. Now both symbol-keyed and string-keyed schemas are detected and normalized correctly. (PR #9 by [@iuhoay](https://github.com/iuhoay))
|
|
12
|
+
- **Shallow key symbolization:** `convert_schema` used `transform_keys` (shallow) which left nested property keys as strings, breaking downstream `MCP::Tool::InputSchema` construction. Now uses deep symbolization recursively.
|
|
13
|
+
- **Guard ordering crash:** `convert_schema` and `convert_input_schema` accessed `schema[:type]` before the `schema.is_a?(Hash)` guard, which would raise `NoMethodError` on `nil` input.
|
|
14
|
+
- **Schema detection tightened:** Pre-built schema detection now requires `type == 'object'` and `properties.is_a?(Hash)`, preventing false positives when a simple schema happens to have parameters named `type` and `properties`.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- `ClaudeAgentSDK.deep_symbolize_keys` utility method for recursive hash key symbolization
|
|
18
|
+
|
|
19
|
+
## [0.7.2] - 2026-02-21
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- **Unknown content block crash:** Unrecognized content block types (e.g., `document` blocks from PDF reading) now return `UnknownBlock` instead of raising `MessageParseError`, aligning with the Python SDK's forward-compatible design
|
|
23
|
+
- **Unknown message type crash:** Unrecognized message types now return `nil` (skipped by callers) instead of raising
|
|
24
|
+
- **Empty input schema crash:** Tools with no parameters (`input_schema: {}`) caused `MCP::Tool::InputSchema` validation failure (`required` array must have at least 1 item per JSON Schema draft-04). Now omits `required` when empty.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- `UnknownBlock` type that preserves raw data for unrecognized content block types
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- **Breaking (minor):** `MessageParser.parse` no longer raises `MessageParseError` for unknown message types — returns `nil` instead. If you were rescuing `MessageParseError` to handle unknown types, check for `nil` return values instead.
|
|
31
|
+
- **Breaking (minor):** `MessageParser.parse_content_block` no longer raises `MessageParseError` for unknown content block types — returns `UnknownBlock` instead. Content block iteration using `is_a?` filtering (e.g., `block.is_a?(TextBlock)`) is unaffected.
|
|
32
|
+
|
|
8
33
|
## [0.7.1] - 2026-02-21
|
|
9
34
|
|
|
10
35
|
### Fixed
|
data/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Add this line to your application's Gemfile:
|
|
|
39
39
|
gem 'claude-agent-sdk', github: 'ya-luotao/claude-agent-sdk-ruby'
|
|
40
40
|
|
|
41
41
|
# Or use a stable version from RubyGems
|
|
42
|
-
gem 'claude-agent-sdk', '~> 0.7.
|
|
42
|
+
gem 'claude-agent-sdk', '~> 0.7.3'
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
And then execute:
|
|
@@ -282,6 +282,26 @@ Async do
|
|
|
282
282
|
end.wait
|
|
283
283
|
```
|
|
284
284
|
|
|
285
|
+
### Pre-built JSON Schemas
|
|
286
|
+
|
|
287
|
+
If your schemas come from another library (e.g., [RubyLLM](https://github.com/crmne/ruby_llm)) that deep-stringifies keys, the SDK handles them transparently — both symbol-keyed and string-keyed schemas are accepted and normalized:
|
|
288
|
+
|
|
289
|
+
```ruby
|
|
290
|
+
# Symbol keys (standard Ruby)
|
|
291
|
+
tool = ClaudeAgentSDK.create_tool('save', 'Save a fact', {
|
|
292
|
+
type: 'object',
|
|
293
|
+
properties: { fact: { type: 'string' } },
|
|
294
|
+
required: ['fact']
|
|
295
|
+
}) { |args| { content: [{ type: 'text', text: "Saved: #{args[:fact]}" }] } }
|
|
296
|
+
|
|
297
|
+
# String keys (e.g., from RubyLLM or JSON.parse)
|
|
298
|
+
tool = ClaudeAgentSDK.create_tool('save', 'Save a fact', {
|
|
299
|
+
'type' => 'object',
|
|
300
|
+
'properties' => { 'fact' => { 'type' => 'string' } },
|
|
301
|
+
'required' => ['fact']
|
|
302
|
+
}) { |args| { content: [{ type: 'text', text: "Saved: #{args[:fact]}" }] } }
|
|
303
|
+
```
|
|
304
|
+
|
|
285
305
|
### Benefits Over External MCP Servers
|
|
286
306
|
|
|
287
307
|
- **No subprocess management** - Runs in the same process as your application
|
|
@@ -978,7 +998,7 @@ end
|
|
|
978
998
|
|
|
979
999
|
```ruby
|
|
980
1000
|
# Union type of all content blocks
|
|
981
|
-
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock
|
|
1001
|
+
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock | UnknownBlock
|
|
982
1002
|
```
|
|
983
1003
|
|
|
984
1004
|
#### TextBlock
|
|
@@ -1026,6 +1046,17 @@ class ToolResultBlock
|
|
|
1026
1046
|
end
|
|
1027
1047
|
```
|
|
1028
1048
|
|
|
1049
|
+
#### UnknownBlock
|
|
1050
|
+
|
|
1051
|
+
Generic content block for types the SDK doesn't explicitly handle (e.g., `document` for PDFs, `image` for inline images). Preserves the raw data for forward compatibility with newer CLI versions.
|
|
1052
|
+
|
|
1053
|
+
```ruby
|
|
1054
|
+
class UnknownBlock
|
|
1055
|
+
attr_accessor :type, # String — the original block type (e.g., "document")
|
|
1056
|
+
:data # Hash — the full raw block hash
|
|
1057
|
+
end
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1029
1060
|
### Error Types
|
|
1030
1061
|
|
|
1031
1062
|
```ruby
|
|
@@ -25,9 +25,9 @@ module ClaudeAgentSDK
|
|
|
25
25
|
parse_stream_event(data)
|
|
26
26
|
when 'rate_limit_event'
|
|
27
27
|
parse_rate_limit_event(data)
|
|
28
|
-
else
|
|
29
|
-
raise MessageParseError.new("Unknown message type: #{message_type}", data: data)
|
|
30
28
|
end
|
|
29
|
+
# Forward-compatible: returns nil for unrecognized message types so
|
|
30
|
+
# newer CLI versions don't crash older SDK versions.
|
|
31
31
|
rescue KeyError => e
|
|
32
32
|
raise MessageParseError.new("Missing required field: #{e.message}", data: data)
|
|
33
33
|
end
|
|
@@ -115,7 +115,9 @@ module ClaudeAgentSDK
|
|
|
115
115
|
is_error: block[:is_error]
|
|
116
116
|
)
|
|
117
117
|
else
|
|
118
|
-
|
|
118
|
+
# Forward-compatible: preserve unrecognized content block types (e.g., "document", "image")
|
|
119
|
+
# so newer CLI versions don't crash older SDK versions.
|
|
120
|
+
UnknownBlock.new(type: block[:type], data: block)
|
|
119
121
|
end
|
|
120
122
|
end
|
|
121
123
|
end
|
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
require 'mcp'
|
|
4
4
|
|
|
5
5
|
module ClaudeAgentSDK
|
|
6
|
+
# Recursively convert all hash keys to symbols
|
|
7
|
+
def self.deep_symbolize_keys(obj)
|
|
8
|
+
case obj
|
|
9
|
+
when Hash then obj.transform_keys(&:to_sym).transform_values { |v| deep_symbolize_keys(v) }
|
|
10
|
+
when Array then obj.map { |v| deep_symbolize_keys(v) }
|
|
11
|
+
else obj
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
6
15
|
# SDK MCP Server - wraps official MCP::Server with block-based API
|
|
7
16
|
#
|
|
8
17
|
# Unlike external MCP servers that run as separate processes, SDK MCP servers
|
|
@@ -164,10 +173,9 @@ module ClaudeAgentSDK
|
|
|
164
173
|
|
|
165
174
|
def input_schema_value
|
|
166
175
|
schema = convert_schema(@tool_def.input_schema)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
)
|
|
176
|
+
opts = { properties: schema[:properties] || {} }
|
|
177
|
+
opts[:required] = schema[:required] if schema[:required]&.any?
|
|
178
|
+
MCP::Tool::InputSchema.new(**opts)
|
|
171
179
|
end
|
|
172
180
|
|
|
173
181
|
def call(server_context: nil, **args)
|
|
@@ -192,9 +200,12 @@ module ClaudeAgentSDK
|
|
|
192
200
|
private
|
|
193
201
|
|
|
194
202
|
def convert_schema(schema)
|
|
195
|
-
# If it's already a proper JSON schema,
|
|
196
|
-
|
|
197
|
-
|
|
203
|
+
# If it's already a proper JSON schema (symbol or string keys), normalize
|
|
204
|
+
# to symbol keys so downstream code (schema[:properties]) works uniformly.
|
|
205
|
+
if schema.is_a?(Hash)
|
|
206
|
+
type_val = schema[:type] || schema['type']
|
|
207
|
+
props_val = schema[:properties] || schema['properties']
|
|
208
|
+
return ClaudeAgentSDK.deep_symbolize_keys(schema) if type_val == 'object' && props_val.is_a?(Hash)
|
|
198
209
|
end
|
|
199
210
|
|
|
200
211
|
# Simple schema: hash mapping parameter names to types
|
|
@@ -204,11 +215,10 @@ module ClaudeAgentSDK
|
|
|
204
215
|
properties[param_name] = type_to_json_schema(param_type)
|
|
205
216
|
end
|
|
206
217
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
218
|
+
result = { type: 'object', properties: properties }
|
|
219
|
+
required_keys = properties.keys.map(&:to_s)
|
|
220
|
+
result[:required] = required_keys unless required_keys.empty?
|
|
221
|
+
return result
|
|
212
222
|
end
|
|
213
223
|
|
|
214
224
|
# Default fallback
|
|
@@ -320,9 +330,12 @@ module ClaudeAgentSDK
|
|
|
320
330
|
end
|
|
321
331
|
|
|
322
332
|
def convert_input_schema(schema)
|
|
323
|
-
# If it's already a proper JSON schema,
|
|
324
|
-
|
|
325
|
-
|
|
333
|
+
# If it's already a proper JSON schema (symbol or string keys), normalize
|
|
334
|
+
# to symbol keys for consistent output.
|
|
335
|
+
if schema.is_a?(Hash)
|
|
336
|
+
type_val = schema[:type] || schema['type']
|
|
337
|
+
props_val = schema[:properties] || schema['properties']
|
|
338
|
+
return ClaudeAgentSDK.deep_symbolize_keys(schema) if type_val == 'object' && props_val.is_a?(Hash)
|
|
326
339
|
end
|
|
327
340
|
|
|
328
341
|
# Simple schema: hash mapping parameter names to types
|
|
@@ -332,11 +345,9 @@ module ClaudeAgentSDK
|
|
|
332
345
|
properties[param_name] = type_to_json_schema(param_type)
|
|
333
346
|
end
|
|
334
347
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
required: properties.keys
|
|
339
|
-
}
|
|
348
|
+
result = { type: 'object', properties: properties }
|
|
349
|
+
result[:required] = properties.keys unless properties.empty?
|
|
350
|
+
return result
|
|
340
351
|
end
|
|
341
352
|
|
|
342
353
|
# Default fallback
|
|
@@ -77,6 +77,17 @@ module ClaudeAgentSDK
|
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
# Generic content block for types the SDK doesn't explicitly handle (e.g., "document", "image").
|
|
81
|
+
# Preserves the raw hash data for forward compatibility with newer CLI versions.
|
|
82
|
+
class UnknownBlock
|
|
83
|
+
attr_accessor :type, :data
|
|
84
|
+
|
|
85
|
+
def initialize(type:, data:)
|
|
86
|
+
@type = type
|
|
87
|
+
@data = data
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
80
91
|
# Message Types
|
|
81
92
|
|
|
82
93
|
# User message
|
data/lib/claude_agent_sdk.rb
CHANGED
|
@@ -119,7 +119,7 @@ module ClaudeAgentSDK
|
|
|
119
119
|
# Read and yield messages from the query handler (filters out control messages)
|
|
120
120
|
query_handler.receive_messages do |data|
|
|
121
121
|
message = MessageParser.parse(data)
|
|
122
|
-
block.call(message)
|
|
122
|
+
block.call(message) if message
|
|
123
123
|
end
|
|
124
124
|
ensure
|
|
125
125
|
# query_handler.close stops the background read task and closes the transport
|
|
@@ -275,7 +275,7 @@ module ClaudeAgentSDK
|
|
|
275
275
|
|
|
276
276
|
@query_handler.receive_messages do |data|
|
|
277
277
|
message = MessageParser.parse(data)
|
|
278
|
-
block.call(message)
|
|
278
|
+
block.call(message) if message
|
|
279
279
|
end
|
|
280
280
|
end
|
|
281
281
|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: claude-agent-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Community Contributors
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-02-
|
|
10
|
+
date: 2026-02-25 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: async
|