claude-agent-sdk 0.1.2 → 0.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5f1881eb9fa20f805736f864736977c80f839d88042b3ba6e36ac23f7550548
4
- data.tar.gz: 2ccee5ba2a1d59d312737eac4a36464cd15a160c3989b4a5d663ef49c7962b9e
3
+ metadata.gz: 4d8b669f4c3f02dea0763113551838bb0a3a5ec514125876f28dbc5d23ee44aa
4
+ data.tar.gz: 6ce126558a2d99e3b6aba03527d5b4ce8954ff3a99a13ee25f54dd6fa0141980
5
5
  SHA512:
6
- metadata.gz: 23cb7eff0c9f6a48ba1599c793cdabef7f8f98524c0a6eaf07abbb62407e4363a6e0d63f5764b1383ae8297b389a7db042ed9670a5e43c6d5e263321a42f9361
7
- data.tar.gz: fa7e760167ff757870f54c6f0175f25bb3d9480843053ae82d9a95c82c6dd29ad478f3901496c72a2bc74cdca1d596db25ddaef09a724df3743005fec907ca94
6
+ metadata.gz: 54e48e83a98845d56d6dc4c1843ebf8728fd74ffd3e7670e826c0fdbf3ef297359c18f5b27c2fac0757da0fab5e28d6c19ab084ac1842c5615d06cf52a0a94f8
7
+ data.tar.gz: 224aca0535d196fa3b348da69af9e4cbcc46276af8d36b7b58f6e35811704f93506ff31df7449e9aae9ebd252c8b5c035fa581dc4d500a5bade82d0d15f37ddc
data/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ 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.1.3] - 2025-10-15
9
+
10
+ ### Added
11
+ - **MCP resource support:** Full support for MCP resources (list, read, subscribe operations)
12
+ - **MCP prompt support:** Support for MCP prompts (list, get operations)
13
+ - **Streaming input support:** Added streaming capabilities for input handling
14
+ - Feature complete MCP implementation matching Python SDK functionality
15
+
8
16
  ## [0.1.2] - 2025-10-14
9
17
 
10
18
  ### Fixed
data/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # Claude Agent SDK for Ruby
2
2
 
3
- > **⚠️ DISCLAIMER**: This is an **unofficial, community-maintained** Ruby SDK for Claude Agent. It is not officially supported or maintained by Anthropic. For official SDK support, please refer to the [Python SDK](https://github.com/anthropics/claude-code/tree/main/agent-sdk/python).
3
+ > **⚠️ DISCLAIMER**: This is an **unofficial, community-maintained** Ruby SDK for Claude Agent. It is not officially supported or maintained by Anthropic. For official SDK support, please refer to the [Python SDK](https://docs.claude.com/en/api/agent-sdk/python).
4
4
  >
5
5
  > This implementation is based on the official Python SDK and aims to provide feature parity for Ruby developers. Use at your own risk.
6
6
 
7
7
  Ruby SDK for Claude Agent. See the [Claude Agent SDK documentation](https://docs.anthropic.com/en/docs/claude-code/sdk) for more information.
8
8
 
9
+ [![Gem Version](https://badge.fury.io/rb/claude-agent-sdk.svg?icon=si%3Arubygems)](https://badge.fury.io/rb/claude-agent-sdk)
10
+
9
11
  ## Installation
10
12
 
11
13
  Add this line to your application's Gemfile:
@@ -92,9 +94,48 @@ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
92
94
  )
93
95
  ```
94
96
 
97
+ ### Streaming Input
98
+
99
+ The `query()` function supports streaming input, allowing you to send multiple messages dynamically instead of a single prompt string.
100
+
101
+ ```ruby
102
+ require 'claude_agent_sdk'
103
+
104
+ # Create a stream of messages
105
+ messages = ['Hello!', 'What is 2+2?', 'Thanks!']
106
+ stream = ClaudeAgentSDK::Streaming.from_array(messages)
107
+
108
+ # Query with streaming input
109
+ ClaudeAgentSDK.query(prompt: stream) do |message|
110
+ puts message if message.is_a?(ClaudeAgentSDK::AssistantMessage)
111
+ end
112
+ ```
113
+
114
+ You can also create custom streaming enumerators:
115
+
116
+ ```ruby
117
+ # Dynamic message generation
118
+ stream = Enumerator.new do |yielder|
119
+ yielder << ClaudeAgentSDK::Streaming.user_message("First message")
120
+ # Do some processing...
121
+ yielder << ClaudeAgentSDK::Streaming.user_message("Second message")
122
+ yielder << ClaudeAgentSDK::Streaming.user_message("Third message")
123
+ end
124
+
125
+ ClaudeAgentSDK.query(prompt: stream) do |message|
126
+ # Process responses
127
+ end
128
+ ```
129
+
130
+ For a complete example, see [examples/streaming_input_example.rb](examples/streaming_input_example.rb).
131
+
95
132
  ## Client
96
133
 
97
- `ClaudeAgentSDK::Client` supports bidirectional, interactive conversations with Claude Code. Unlike `query()`, `Client` enables **custom tools**, **hooks**, and **permission callbacks**, all of which can be defined as Ruby procs/lambdas. See [lib/claude_agent_sdk.rb](lib/claude_agent_sdk.rb).
134
+ `ClaudeAgentSDK::Client` supports bidirectional, interactive conversations with Claude Code. Unlike `query()`, `Client` enables **custom tools**, **hooks**, and **permission callbacks**, all of which can be defined as Ruby procs/lambdas.
135
+
136
+ **The Client class automatically uses streaming mode** for bidirectional communication, allowing you to send multiple queries dynamically during a single session without closing the connection. This matches the Python SDK's `ClaudeSDKClient` behavior.
137
+
138
+ See [lib/claude_agent_sdk.rb](lib/claude_agent_sdk.rb) for implementation details.
98
139
 
99
140
  ### Custom Tools (SDK MCP Servers)
100
141
 
@@ -193,6 +234,58 @@ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
193
234
  )
194
235
  ```
195
236
 
237
+ #### MCP Resources and Prompts
238
+
239
+ SDK MCP servers can also expose **resources** (data sources) and **prompts** (reusable templates):
240
+
241
+ ```ruby
242
+ # Create a resource (data source Claude can read)
243
+ config_resource = ClaudeAgentSDK.create_resource(
244
+ uri: 'config://app/settings',
245
+ name: 'Application Settings',
246
+ description: 'Current app configuration',
247
+ mime_type: 'application/json'
248
+ ) do
249
+ config_data = { app_name: 'MyApp', version: '1.0.0' }
250
+ {
251
+ contents: [{
252
+ uri: 'config://app/settings',
253
+ mimeType: 'application/json',
254
+ text: JSON.pretty_generate(config_data)
255
+ }]
256
+ }
257
+ end
258
+
259
+ # Create a prompt template
260
+ review_prompt = ClaudeAgentSDK.create_prompt(
261
+ name: 'code_review',
262
+ description: 'Review code for best practices',
263
+ arguments: [
264
+ { name: 'code', description: 'Code to review', required: true }
265
+ ]
266
+ ) do |args|
267
+ {
268
+ messages: [{
269
+ role: 'user',
270
+ content: {
271
+ type: 'text',
272
+ text: "Review this code: #{args[:code]}"
273
+ }
274
+ }]
275
+ }
276
+ end
277
+
278
+ # Create server with tools, resources, and prompts
279
+ server = ClaudeAgentSDK.create_sdk_mcp_server(
280
+ name: 'dev-tools',
281
+ tools: [my_tool],
282
+ resources: [config_resource],
283
+ prompts: [review_prompt]
284
+ )
285
+ ```
286
+
287
+ For complete examples, see [examples/mcp_resources_prompts_example.rb](examples/mcp_resources_prompts_example.rb).
288
+
196
289
  ### Basic Client Usage
197
290
 
198
291
  ```ruby
@@ -203,6 +296,7 @@ Async do
203
296
  client = ClaudeAgentSDK::Client.new
204
297
 
205
298
  begin
299
+ # Connect automatically uses streaming mode for bidirectional communication
206
300
  client.connect
207
301
 
208
302
  # Send a query
@@ -415,7 +509,9 @@ See the following examples for complete working code:
415
509
 
416
510
  - [examples/quick_start.rb](examples/quick_start.rb) - Basic `query()` usage with options
417
511
  - [examples/client_example.rb](examples/client_example.rb) - Interactive Client usage
512
+ - [examples/streaming_input_example.rb](examples/streaming_input_example.rb) - Streaming input for multi-turn conversations
418
513
  - [examples/mcp_calculator.rb](examples/mcp_calculator.rb) - Custom tools with SDK MCP servers
514
+ - [examples/mcp_resources_prompts_example.rb](examples/mcp_resources_prompts_example.rb) - MCP resources and prompts
419
515
  - [examples/hooks_example.rb](examples/hooks_example.rb) - Using hooks to control tool execution
420
516
  - [examples/permission_callback_example.rb](examples/permission_callback_example.rb) - Dynamic tool permission control
421
517
 
@@ -423,10 +519,6 @@ See the following examples for complete working code:
423
519
 
424
520
  After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rspec` to run the tests.
425
521
 
426
- ## Contributing
427
-
428
- Bug reports and pull requests are welcome on GitHub at https://github.com/anthropics/claude-agent-sdk-ruby.
429
-
430
522
  ## License
431
523
 
432
524
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -314,6 +314,14 @@ module ClaudeAgentSDK
314
314
  handle_mcp_tools_list(server, message)
315
315
  when 'tools/call'
316
316
  handle_mcp_tools_call(server, message, params)
317
+ when 'resources/list'
318
+ handle_mcp_resources_list(server, message)
319
+ when 'resources/read'
320
+ handle_mcp_resources_read(server, message, params)
321
+ when 'prompts/list'
322
+ handle_mcp_prompts_list(server, message)
323
+ when 'prompts/get'
324
+ handle_mcp_prompts_get(server, message, params)
317
325
  when 'notifications/initialized'
318
326
  { jsonrpc: '2.0', result: {} }
319
327
  else
@@ -332,12 +340,17 @@ module ClaudeAgentSDK
332
340
  end
333
341
 
334
342
  def handle_mcp_initialize(server, message)
343
+ capabilities = {}
344
+ capabilities[:tools] = {} if server.tools && !server.tools.empty?
345
+ capabilities[:resources] = {} if server.resources && !server.resources.empty?
346
+ capabilities[:prompts] = {} if server.prompts && !server.prompts.empty?
347
+
335
348
  {
336
349
  jsonrpc: '2.0',
337
350
  id: message[:id],
338
351
  result: {
339
352
  protocolVersion: '2024-11-05',
340
- capabilities: { tools: {} },
353
+ capabilities: capabilities,
341
354
  serverInfo: {
342
355
  name: server.name,
343
356
  version: server.version || '1.0.0'
@@ -384,6 +397,58 @@ module ClaudeAgentSDK
384
397
  }
385
398
  end
386
399
 
400
+ def handle_mcp_resources_list(server, message)
401
+ # List resources from the SDK MCP server
402
+ resources_data = server.list_resources
403
+ {
404
+ jsonrpc: '2.0',
405
+ id: message[:id],
406
+ result: { resources: resources_data }
407
+ }
408
+ end
409
+
410
+ def handle_mcp_resources_read(server, message, params)
411
+ # Read a resource from the SDK MCP server
412
+ uri = params[:uri]
413
+ raise 'Missing uri parameter for resources/read' unless uri
414
+
415
+ # Read the resource
416
+ result = server.read_resource(uri)
417
+
418
+ {
419
+ jsonrpc: '2.0',
420
+ id: message[:id],
421
+ result: result
422
+ }
423
+ end
424
+
425
+ def handle_mcp_prompts_list(server, message)
426
+ # List prompts from the SDK MCP server
427
+ prompts_data = server.list_prompts
428
+ {
429
+ jsonrpc: '2.0',
430
+ id: message[:id],
431
+ result: { prompts: prompts_data }
432
+ }
433
+ end
434
+
435
+ def handle_mcp_prompts_get(server, message, params)
436
+ # Get a prompt from the SDK MCP server
437
+ name = params[:name]
438
+ raise 'Missing name parameter for prompts/get' unless name
439
+
440
+ arguments = params[:arguments] || {}
441
+
442
+ # Get the prompt
443
+ result = server.get_prompt(name, arguments)
444
+
445
+ {
446
+ jsonrpc: '2.0',
447
+ id: message[:id],
448
+ result: result
449
+ }
450
+ end
451
+
387
452
  public
388
453
 
389
454
  # Send interrupt control request
@@ -6,14 +6,23 @@ module ClaudeAgentSDK
6
6
  # Unlike external MCP servers that run as separate processes, SDK MCP servers
7
7
  # run directly in your application's process, providing better performance
8
8
  # and simpler deployment.
9
+ #
10
+ # Supports:
11
+ # - Tools: Executable functions that Claude can call
12
+ # - Resources: Data sources that can be read (files, databases, APIs, etc.)
13
+ # - Prompts: Reusable prompt templates with arguments
9
14
  class SdkMcpServer
10
- attr_reader :name, :version, :tools
15
+ attr_reader :name, :version, :tools, :resources, :prompts
11
16
 
12
- def initialize(name:, version: '1.0.0', tools: [])
17
+ def initialize(name:, version: '1.0.0', tools: [], resources: [], prompts: [])
13
18
  @name = name
14
19
  @version = version
15
20
  @tools = tools
21
+ @resources = resources
22
+ @prompts = prompts
16
23
  @tool_map = tools.each_with_object({}) { |tool, hash| hash[tool.name] = tool }
24
+ @resource_map = resources.each_with_object({}) { |res, hash| hash[res.uri] = res }
25
+ @prompt_map = prompts.each_with_object({}) { |prompt, hash| hash[prompt.name] = prompt }
17
26
  end
18
27
 
19
28
  # List all available tools
@@ -47,6 +56,68 @@ module ClaudeAgentSDK
47
56
  result
48
57
  end
49
58
 
59
+ # List all available resources
60
+ # @return [Array<Hash>] Array of resource definitions
61
+ def list_resources
62
+ @resources.map do |resource|
63
+ {
64
+ uri: resource.uri,
65
+ name: resource.name,
66
+ description: resource.description,
67
+ mimeType: resource.mime_type
68
+ }.compact
69
+ end
70
+ end
71
+
72
+ # Read a resource by URI
73
+ # @param uri [String] Resource URI
74
+ # @return [Hash] Resource content
75
+ def read_resource(uri)
76
+ resource = @resource_map[uri]
77
+ raise "Resource '#{uri}' not found" unless resource
78
+
79
+ # Call the resource's reader
80
+ content = resource.reader.call
81
+
82
+ # Ensure content has the expected format
83
+ unless content.is_a?(Hash) && content[:contents]
84
+ raise "Resource '#{uri}' must return a hash with :contents key"
85
+ end
86
+
87
+ content
88
+ end
89
+
90
+ # List all available prompts
91
+ # @return [Array<Hash>] Array of prompt definitions
92
+ def list_prompts
93
+ @prompts.map do |prompt|
94
+ {
95
+ name: prompt.name,
96
+ description: prompt.description,
97
+ arguments: prompt.arguments
98
+ }.compact
99
+ end
100
+ end
101
+
102
+ # Get a prompt by name
103
+ # @param name [String] Prompt name
104
+ # @param arguments [Hash] Arguments to fill in the prompt template
105
+ # @return [Hash] Prompt with filled-in arguments
106
+ def get_prompt(name, arguments = {})
107
+ prompt = @prompt_map[name]
108
+ raise "Prompt '#{name}' not found" unless prompt
109
+
110
+ # Call the prompt's generator
111
+ result = prompt.generator.call(arguments)
112
+
113
+ # Ensure result has the expected format
114
+ unless result.is_a?(Hash) && result[:messages]
115
+ raise "Prompt '#{name}' must return a hash with :messages key"
116
+ end
117
+
118
+ result
119
+ end
120
+
50
121
  private
51
122
 
52
123
  def convert_input_schema(schema)
@@ -130,11 +201,123 @@ module ClaudeAgentSDK
130
201
  )
131
202
  end
132
203
 
204
+ # Helper function to create a resource definition
205
+ #
206
+ # @param uri [String] Unique identifier for the resource (e.g., "file:///path/to/file")
207
+ # @param name [String] Human-readable name
208
+ # @param description [String, nil] Optional description
209
+ # @param mime_type [String, nil] Optional MIME type (e.g., "text/plain", "application/json")
210
+ # @param reader [Proc] Block that returns the resource content
211
+ # @return [SdkMcpResource] Resource definition
212
+ #
213
+ # @example File resource
214
+ # resource = create_resource(
215
+ # uri: 'file:///config/settings.json',
216
+ # name: 'Application Settings',
217
+ # description: 'Current application configuration',
218
+ # mime_type: 'application/json'
219
+ # ) do
220
+ # content = File.read('/path/to/settings.json')
221
+ # {
222
+ # contents: [{
223
+ # uri: 'file:///config/settings.json',
224
+ # mimeType: 'application/json',
225
+ # text: content
226
+ # }]
227
+ # }
228
+ # end
229
+ #
230
+ # @example Database resource
231
+ # resource = create_resource(
232
+ # uri: 'db://users/count',
233
+ # name: 'User Count',
234
+ # description: 'Total number of registered users'
235
+ # ) do
236
+ # count = User.count
237
+ # {
238
+ # contents: [{
239
+ # uri: 'db://users/count',
240
+ # mimeType: 'text/plain',
241
+ # text: count.to_s
242
+ # }]
243
+ # }
244
+ # end
245
+ def self.create_resource(uri:, name:, description: nil, mime_type: nil, &reader)
246
+ raise ArgumentError, 'Block required for resource reader' unless reader
247
+
248
+ SdkMcpResource.new(
249
+ uri: uri,
250
+ name: name,
251
+ description: description,
252
+ mime_type: mime_type,
253
+ reader: reader
254
+ )
255
+ end
256
+
257
+ # Helper function to create a prompt definition
258
+ #
259
+ # @param name [String] Unique identifier for the prompt
260
+ # @param description [String, nil] Optional description
261
+ # @param arguments [Array<Hash>, nil] Optional argument definitions
262
+ # @param generator [Proc] Block that generates prompt messages
263
+ # @return [SdkMcpPrompt] Prompt definition
264
+ #
265
+ # @example Simple prompt
266
+ # prompt = create_prompt(
267
+ # name: 'code_review',
268
+ # description: 'Review code for best practices'
269
+ # ) do |args|
270
+ # {
271
+ # messages: [
272
+ # {
273
+ # role: 'user',
274
+ # content: {
275
+ # type: 'text',
276
+ # text: 'Please review this code for best practices and suggest improvements.'
277
+ # }
278
+ # }
279
+ # ]
280
+ # }
281
+ # end
282
+ #
283
+ # @example Prompt with arguments
284
+ # prompt = create_prompt(
285
+ # name: 'git_commit',
286
+ # description: 'Generate a git commit message',
287
+ # arguments: [
288
+ # { name: 'changes', description: 'Description of changes', required: true }
289
+ # ]
290
+ # ) do |args|
291
+ # {
292
+ # messages: [
293
+ # {
294
+ # role: 'user',
295
+ # content: {
296
+ # type: 'text',
297
+ # text: "Generate a concise git commit message for: #{args[:changes]}"
298
+ # }
299
+ # }
300
+ # ]
301
+ # }
302
+ # end
303
+ def self.create_prompt(name:, description: nil, arguments: nil, &generator)
304
+ raise ArgumentError, 'Block required for prompt generator' unless generator
305
+
306
+ SdkMcpPrompt.new(
307
+ name: name,
308
+ description: description,
309
+ arguments: arguments,
310
+ generator: generator
311
+ )
312
+ end
313
+
133
314
  # Create an SDK MCP server
134
315
  #
135
316
  # @param name [String] Unique identifier for the server
136
317
  # @param version [String] Server version (default: '1.0.0')
137
318
  # @param tools [Array<SdkMcpTool>] List of tool definitions
319
+ # @param resources [Array<SdkMcpResource>] List of resource definitions
320
+ # @param prompts [Array<SdkMcpPrompt>] List of prompt definitions
138
321
  # @return [Hash] MCP server configuration for ClaudeAgentOptions
139
322
  #
140
323
  # @example Simple calculator server
@@ -152,8 +335,31 @@ module ClaudeAgentSDK
152
335
  # mcp_servers: { calc: calculator },
153
336
  # allowed_tools: ['mcp__calc__add']
154
337
  # )
155
- def self.create_sdk_mcp_server(name:, version: '1.0.0', tools: [])
156
- server = SdkMcpServer.new(name: name, version: version, tools: tools)
338
+ #
339
+ # @example Server with resources and prompts
340
+ # config_resource = ClaudeAgentSDK.create_resource(
341
+ # uri: 'config://app',
342
+ # name: 'App Config'
343
+ # ) { { contents: [{ uri: 'config://app', text: 'config data' }] } }
344
+ #
345
+ # review_prompt = ClaudeAgentSDK.create_prompt(
346
+ # name: 'review',
347
+ # description: 'Code review'
348
+ # ) { { messages: [{ role: 'user', content: { type: 'text', text: 'Review this' } }] } }
349
+ #
350
+ # server = ClaudeAgentSDK.create_sdk_mcp_server(
351
+ # name: 'dev-tools',
352
+ # resources: [config_resource],
353
+ # prompts: [review_prompt]
354
+ # )
355
+ def self.create_sdk_mcp_server(name:, version: '1.0.0', tools: [], resources: [], prompts: [])
356
+ server = SdkMcpServer.new(
357
+ name: name,
358
+ version: version,
359
+ tools: tools,
360
+ resources: resources,
361
+ prompts: prompts
362
+ )
157
363
 
158
364
  # Return configuration for ClaudeAgentOptions
159
365
  {
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module ClaudeAgentSDK
6
+ # Streaming input helpers for Claude Agent SDK
7
+ module Streaming
8
+ # Create a user message for streaming input
9
+ #
10
+ # @param content [String] The message content
11
+ # @param session_id [String] Session identifier
12
+ # @param parent_tool_use_id [String, nil] Parent tool use ID if responding to a tool
13
+ # @return [String] JSON-encoded message
14
+ def self.user_message(content, session_id: 'default', parent_tool_use_id: nil)
15
+ message = {
16
+ type: 'user',
17
+ message: {
18
+ role: 'user',
19
+ content: content
20
+ },
21
+ parent_tool_use_id: parent_tool_use_id,
22
+ session_id: session_id
23
+ }
24
+ JSON.generate(message) + "\n"
25
+ end
26
+
27
+ # Create an Enumerator from an array of messages
28
+ #
29
+ # @param messages [Array<String>] Array of message strings
30
+ # @param session_id [String] Session identifier
31
+ # @return [Enumerator] Enumerator yielding JSON-encoded messages
32
+ #
33
+ # @example
34
+ # messages = ['Hello', 'What is 2+2?', 'Thanks!']
35
+ # stream = ClaudeAgentSDK::Streaming.from_array(messages)
36
+ def self.from_array(messages, session_id: 'default')
37
+ Enumerator.new do |yielder|
38
+ messages.each do |content|
39
+ yielder << user_message(content, session_id: session_id)
40
+ end
41
+ end
42
+ end
43
+
44
+ # Create an Enumerator from a block
45
+ #
46
+ # @yield Block that yields message strings
47
+ # @param session_id [String] Session identifier
48
+ # @return [Enumerator] Enumerator yielding JSON-encoded messages
49
+ #
50
+ # @example
51
+ # stream = ClaudeAgentSDK::Streaming.from_block do |yielder|
52
+ # yielder.yield('First message')
53
+ # sleep 1
54
+ # yielder.yield('Second message')
55
+ # end
56
+ def self.from_block(session_id: 'default', &block)
57
+ Enumerator.new do |yielder|
58
+ collector = Object.new
59
+ def collector.yield(content)
60
+ @content = content
61
+ end
62
+ def collector.content
63
+ @content
64
+ end
65
+
66
+ inner_enum = Enumerator.new(&block)
67
+ inner_enum.each do |content|
68
+ yielder << user_message(content, session_id: session_id)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -355,4 +355,29 @@ module ClaudeAgentSDK
355
355
  @handler = handler
356
356
  end
357
357
  end
358
+
359
+ # SDK MCP Resource definition
360
+ class SdkMcpResource
361
+ attr_accessor :uri, :name, :description, :mime_type, :reader
362
+
363
+ def initialize(uri:, name:, description: nil, mime_type: nil, reader:)
364
+ @uri = uri
365
+ @name = name
366
+ @description = description
367
+ @mime_type = mime_type
368
+ @reader = reader
369
+ end
370
+ end
371
+
372
+ # SDK MCP Prompt definition
373
+ class SdkMcpPrompt
374
+ attr_accessor :name, :description, :arguments, :generator
375
+
376
+ def initialize(name:, description: nil, arguments: nil, generator:)
377
+ @name = name
378
+ @description = description
379
+ @arguments = arguments
380
+ @generator = generator
381
+ end
382
+ end
358
383
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeAgentSDK
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.4'
5
5
  end
@@ -8,6 +8,7 @@ require_relative 'claude_agent_sdk/subprocess_cli_transport'
8
8
  require_relative 'claude_agent_sdk/message_parser'
9
9
  require_relative 'claude_agent_sdk/query'
10
10
  require_relative 'claude_agent_sdk/sdk_mcp_server'
11
+ require_relative 'claude_agent_sdk/streaming'
11
12
  require 'async'
12
13
  require 'securerandom'
13
14
 
@@ -18,7 +19,7 @@ module ClaudeAgentSDK
18
19
  # This function is ideal for simple, stateless queries where you don't need
19
20
  # bidirectional communication or conversation management.
20
21
  #
21
- # @param prompt [String] The prompt to send to Claude
22
+ # @param prompt [String, Enumerator] The prompt to send to Claude, or an Enumerator for streaming input
22
23
  # @param options [ClaudeAgentOptions] Optional configuration
23
24
  # @yield [Message] Each message from the conversation
24
25
  # @return [Enumerator] if no block given
@@ -40,6 +41,12 @@ module ClaudeAgentSDK
40
41
  # end
41
42
  # end
42
43
  # end
44
+ #
45
+ # @example Streaming input
46
+ # messages = Streaming.from_array(['Hello', 'What is 2+2?', 'Thanks!'])
47
+ # ClaudeAgentSDK.query(prompt: messages) do |message|
48
+ # puts message
49
+ # end
43
50
  def self.query(prompt:, options: nil, &block)
44
51
  return enum_for(:query, prompt: prompt, options: options) unless block
45
52
 
@@ -50,6 +57,21 @@ module ClaudeAgentSDK
50
57
  transport = SubprocessCLITransport.new(prompt, options)
51
58
  begin
52
59
  transport.connect
60
+
61
+ # If prompt is an Enumerator, write each message to stdin
62
+ if prompt.is_a?(Enumerator) || prompt.respond_to?(:each)
63
+ Async do
64
+ begin
65
+ prompt.each do |message_json|
66
+ transport.write(message_json)
67
+ end
68
+ ensure
69
+ transport.end_input
70
+ end
71
+ end
72
+ end
73
+
74
+ # Read and yield messages
53
75
  transport.read_messages do |data|
54
76
  message = MessageParser.parse(data)
55
77
  block.call(message)
@@ -64,11 +86,12 @@ module ClaudeAgentSDK
64
86
  #
65
87
  # This client provides full control over the conversation flow with support
66
88
  # for streaming, hooks, permission callbacks, and dynamic message sending.
89
+ # The Client class always uses streaming mode for bidirectional communication.
67
90
  #
68
91
  # @example Basic usage
69
92
  # Async do
70
93
  # client = ClaudeAgentSDK::Client.new
71
- # client.connect
94
+ # client.connect # No arguments needed - automatically uses streaming mode
72
95
  #
73
96
  # client.query("What is the capital of France?")
74
97
  # client.receive_response do |msg|
@@ -128,8 +151,10 @@ module ClaudeAgentSDK
128
151
  configured_options = @options.dup_with(permission_prompt_tool_name: 'stdio')
129
152
  end
130
153
 
131
- # Create transport
132
- actual_prompt = prompt || ''
154
+ # Auto-connect with empty enumerator if no prompt is provided
155
+ # This matches the Python SDK pattern where ClaudeSDKClient always uses streaming mode
156
+ # An empty enumerator keeps stdin open for bidirectional communication
157
+ actual_prompt = prompt || [].to_enum
133
158
  @transport = SubprocessCLITransport.new(actual_prompt, configured_options)
134
159
  @transport.connect
135
160
 
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.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Community Contributors
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-10-14 00:00:00.000000000 Z
10
+ date: 2025-10-17 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: async
@@ -94,6 +94,7 @@ files:
94
94
  - lib/claude_agent_sdk/message_parser.rb
95
95
  - lib/claude_agent_sdk/query.rb
96
96
  - lib/claude_agent_sdk/sdk_mcp_server.rb
97
+ - lib/claude_agent_sdk/streaming.rb
97
98
  - lib/claude_agent_sdk/subprocess_cli_transport.rb
98
99
  - lib/claude_agent_sdk/transport.rb
99
100
  - lib/claude_agent_sdk/types.rb