claude-agent-sdk 0.1.4 → 0.2.1
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 +22 -0
- data/README.md +4 -2
- data/lib/claude_agent_sdk/query.rb +5 -2
- data/lib/claude_agent_sdk/sdk_mcp_server.rb +211 -18
- data/lib/claude_agent_sdk/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db20a5dbe6ebc51b229f4de595489038a26a47d8109600131c45aa2bf27c6d2e
|
|
4
|
+
data.tar.gz: 563e16e88a48ce331cd48c6ebce3302f0c57292ee6563ebaec7af02bed29433d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 288868f49db2968d92dc2250fe2ca91b7692431c92dc4fce41b04a35610858c146490da45178290877988a9210d67ebdf8257363f651e058a0bab534d24222fc
|
|
7
|
+
data.tar.gz: 183224c382c68b916da22c20c64d2f4fd625861baee265611bae8f752c25effcc7de2159a61cc65822cd62f24344f85b485cd1e6005ceff0e88ea2c34be4c30c
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ 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.2.0] - 2025-10-17
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **BREAKING:** Updated minimum Ruby version from 3.0+ to 3.2+ (required by official MCP SDK)
|
|
12
|
+
- **Major refactoring:** SDK MCP server now uses official Ruby MCP SDK (`mcp` gem v0.4) internally
|
|
13
|
+
- Internal implementation migrated from custom MCP to wrapping official `MCP::Server`
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Official Ruby MCP SDK (`mcp` gem) as runtime dependency
|
|
17
|
+
- Full MCP protocol compliance via official SDK
|
|
18
|
+
- `handle_json` method for protocol-compliant JSON-RPC handling
|
|
19
|
+
|
|
20
|
+
### Improved
|
|
21
|
+
- Better long-term maintenance by leveraging official SDK updates
|
|
22
|
+
- Aligned with Python SDK implementation pattern (using official MCP library)
|
|
23
|
+
- All 86 tests passing with full backward compatibility maintained
|
|
24
|
+
|
|
25
|
+
### Technical Details
|
|
26
|
+
- Creates dynamic `MCP::Tool`, `MCP::Resource`, and `MCP::Prompt` classes from block-based definitions
|
|
27
|
+
- User-facing API remains unchanged - no breaking changes for Ruby 3.2+ users
|
|
28
|
+
- Maintains backward-compatible methods (`list_tools`, `call_tool`, etc.)
|
|
29
|
+
|
|
8
30
|
## [0.1.3] - 2025-10-15
|
|
9
31
|
|
|
10
32
|
### Added
|
data/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Ruby SDK for Claude Agent. See the [Claude Agent SDK documentation](https://docs
|
|
|
13
13
|
Add this line to your application's Gemfile:
|
|
14
14
|
|
|
15
15
|
```ruby
|
|
16
|
-
gem 'claude-agent-sdk'
|
|
16
|
+
gem 'claude-agent-sdk', '~> 0.2.0'
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
And then execute:
|
|
@@ -29,7 +29,7 @@ gem install claude-agent-sdk
|
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
**Prerequisites:**
|
|
32
|
-
- Ruby 3.
|
|
32
|
+
- Ruby 3.2+
|
|
33
33
|
- Node.js
|
|
34
34
|
- Claude Code 2.0.0+: `npm install -g @anthropic-ai/claude-code`
|
|
35
35
|
|
|
@@ -143,6 +143,8 @@ A **custom tool** is a Ruby proc/lambda that you can offer to Claude, for Claude
|
|
|
143
143
|
|
|
144
144
|
Custom tools are implemented as in-process MCP servers that run directly within your Ruby application, eliminating the need for separate processes that regular MCP servers require.
|
|
145
145
|
|
|
146
|
+
**Implementation**: This SDK uses the [official Ruby MCP SDK](https://github.com/modelcontextprotocol/ruby-sdk) (`mcp` gem) internally, providing full protocol compliance while offering a simpler block-based API for tool definition.
|
|
147
|
+
|
|
146
148
|
For a complete example, see [examples/mcp_calculator.rb](examples/mcp_calculator.rb).
|
|
147
149
|
|
|
148
150
|
#### Creating a Simple Tool
|
|
@@ -292,7 +292,10 @@ module ClaudeAgentSDK
|
|
|
292
292
|
end
|
|
293
293
|
|
|
294
294
|
def handle_sdk_mcp_request(server_name, message)
|
|
295
|
-
|
|
295
|
+
# Convert server_name to symbol if needed for hash lookup
|
|
296
|
+
server_key = @sdk_mcp_servers.key?(server_name) ? server_name : server_name.to_sym
|
|
297
|
+
|
|
298
|
+
unless @sdk_mcp_servers.key?(server_key)
|
|
296
299
|
return {
|
|
297
300
|
jsonrpc: '2.0',
|
|
298
301
|
id: message[:id],
|
|
@@ -303,7 +306,7 @@ module ClaudeAgentSDK
|
|
|
303
306
|
}
|
|
304
307
|
end
|
|
305
308
|
|
|
306
|
-
server = @sdk_mcp_servers[
|
|
309
|
+
server = @sdk_mcp_servers[server_key]
|
|
307
310
|
method = message[:method]
|
|
308
311
|
params = message[:params] || {}
|
|
309
312
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'mcp'
|
|
4
|
+
|
|
3
5
|
module ClaudeAgentSDK
|
|
4
|
-
# SDK MCP Server -
|
|
6
|
+
# SDK MCP Server - wraps official MCP::Server with block-based API
|
|
5
7
|
#
|
|
6
8
|
# Unlike external MCP servers that run as separate processes, SDK MCP servers
|
|
7
9
|
# run directly in your application's process, providing better performance
|
|
8
10
|
# and simpler deployment.
|
|
9
11
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# - Resources: Data sources that can be read (files, databases, APIs, etc.)
|
|
13
|
-
# - Prompts: Reusable prompt templates with arguments
|
|
12
|
+
# This class wraps the official MCP Ruby SDK and provides a simpler block-based
|
|
13
|
+
# API for defining tools, resources, and prompts.
|
|
14
14
|
class SdkMcpServer
|
|
15
|
-
attr_reader :name, :version, :tools, :resources, :prompts
|
|
15
|
+
attr_reader :name, :version, :tools, :resources, :prompts, :mcp_server
|
|
16
16
|
|
|
17
17
|
def initialize(name:, version: '1.0.0', tools: [], resources: [], prompts: [])
|
|
18
18
|
@name = name
|
|
@@ -20,12 +20,34 @@ module ClaudeAgentSDK
|
|
|
20
20
|
@tools = tools
|
|
21
21
|
@resources = resources
|
|
22
22
|
@prompts = prompts
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
|
|
24
|
+
# Create dynamic Tool classes from tool definitions
|
|
25
|
+
tool_classes = create_tool_classes(tools)
|
|
26
|
+
|
|
27
|
+
# Create dynamic Resource classes from resource definitions
|
|
28
|
+
resource_classes = create_resource_classes(resources)
|
|
29
|
+
|
|
30
|
+
# Create dynamic Prompt classes from prompt definitions
|
|
31
|
+
prompt_classes = create_prompt_classes(prompts)
|
|
32
|
+
|
|
33
|
+
# Create the official MCP::Server instance
|
|
34
|
+
@mcp_server = MCP::Server.new(
|
|
35
|
+
name: name,
|
|
36
|
+
version: version,
|
|
37
|
+
tools: tool_classes,
|
|
38
|
+
resources: resource_classes,
|
|
39
|
+
prompts: prompt_classes
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Handle a JSON-RPC request
|
|
44
|
+
# @param json_string [String] JSON-RPC request
|
|
45
|
+
# @return [String] JSON-RPC response
|
|
46
|
+
def handle_json(json_string)
|
|
47
|
+
@mcp_server.handle_json(json_string)
|
|
26
48
|
end
|
|
27
49
|
|
|
28
|
-
# List all available tools
|
|
50
|
+
# List all available tools (for backward compatibility)
|
|
29
51
|
# @return [Array<Hash>] Array of tool definitions
|
|
30
52
|
def list_tools
|
|
31
53
|
@tools.map do |tool|
|
|
@@ -37,12 +59,12 @@ module ClaudeAgentSDK
|
|
|
37
59
|
end
|
|
38
60
|
end
|
|
39
61
|
|
|
40
|
-
# Execute a tool by name
|
|
62
|
+
# Execute a tool by name (for backward compatibility)
|
|
41
63
|
# @param name [String] Tool name
|
|
42
64
|
# @param arguments [Hash] Tool arguments
|
|
43
65
|
# @return [Hash] Tool result
|
|
44
66
|
def call_tool(name, arguments)
|
|
45
|
-
tool = @
|
|
67
|
+
tool = @tools.find { |t| t.name == name }
|
|
46
68
|
raise "Tool '#{name}' not found" unless tool
|
|
47
69
|
|
|
48
70
|
# Call the tool's handler
|
|
@@ -56,7 +78,7 @@ module ClaudeAgentSDK
|
|
|
56
78
|
result
|
|
57
79
|
end
|
|
58
80
|
|
|
59
|
-
# List all available resources
|
|
81
|
+
# List all available resources (for backward compatibility)
|
|
60
82
|
# @return [Array<Hash>] Array of resource definitions
|
|
61
83
|
def list_resources
|
|
62
84
|
@resources.map do |resource|
|
|
@@ -69,11 +91,11 @@ module ClaudeAgentSDK
|
|
|
69
91
|
end
|
|
70
92
|
end
|
|
71
93
|
|
|
72
|
-
# Read a resource by URI
|
|
94
|
+
# Read a resource by URI (for backward compatibility)
|
|
73
95
|
# @param uri [String] Resource URI
|
|
74
96
|
# @return [Hash] Resource content
|
|
75
97
|
def read_resource(uri)
|
|
76
|
-
resource = @
|
|
98
|
+
resource = @resources.find { |r| r.uri == uri }
|
|
77
99
|
raise "Resource '#{uri}' not found" unless resource
|
|
78
100
|
|
|
79
101
|
# Call the resource's reader
|
|
@@ -87,7 +109,7 @@ module ClaudeAgentSDK
|
|
|
87
109
|
content
|
|
88
110
|
end
|
|
89
111
|
|
|
90
|
-
# List all available prompts
|
|
112
|
+
# List all available prompts (for backward compatibility)
|
|
91
113
|
# @return [Array<Hash>] Array of prompt definitions
|
|
92
114
|
def list_prompts
|
|
93
115
|
@prompts.map do |prompt|
|
|
@@ -99,12 +121,12 @@ module ClaudeAgentSDK
|
|
|
99
121
|
end
|
|
100
122
|
end
|
|
101
123
|
|
|
102
|
-
# Get a prompt by name
|
|
124
|
+
# Get a prompt by name (for backward compatibility)
|
|
103
125
|
# @param name [String] Prompt name
|
|
104
126
|
# @param arguments [Hash] Arguments to fill in the prompt template
|
|
105
127
|
# @return [Hash] Prompt with filled-in arguments
|
|
106
128
|
def get_prompt(name, arguments = {})
|
|
107
|
-
prompt = @
|
|
129
|
+
prompt = @prompts.find { |p| p.name == name }
|
|
108
130
|
raise "Prompt '#{name}' not found" unless prompt
|
|
109
131
|
|
|
110
132
|
# Call the prompt's generator
|
|
@@ -120,6 +142,177 @@ module ClaudeAgentSDK
|
|
|
120
142
|
|
|
121
143
|
private
|
|
122
144
|
|
|
145
|
+
# Create dynamic Tool classes from tool definitions
|
|
146
|
+
def create_tool_classes(tools)
|
|
147
|
+
tools.map do |tool_def|
|
|
148
|
+
# Create a new class that extends MCP::Tool
|
|
149
|
+
Class.new(MCP::Tool) do
|
|
150
|
+
@tool_def = tool_def
|
|
151
|
+
|
|
152
|
+
class << self
|
|
153
|
+
attr_reader :tool_def
|
|
154
|
+
|
|
155
|
+
def name_value
|
|
156
|
+
@tool_def.name
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def description_value
|
|
160
|
+
@tool_def.description
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def input_schema_value
|
|
164
|
+
schema = convert_schema(@tool_def.input_schema)
|
|
165
|
+
MCP::Tool::InputSchema.new(
|
|
166
|
+
properties: schema[:properties] || {},
|
|
167
|
+
required: schema[:required] || []
|
|
168
|
+
)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def call(server_context: nil, **args)
|
|
172
|
+
# Filter out server_context and pass remaining args to handler
|
|
173
|
+
result = @tool_def.handler.call(args)
|
|
174
|
+
|
|
175
|
+
# Convert result to MCP::Tool::Response format
|
|
176
|
+
content = result[:content].map do |item|
|
|
177
|
+
{
|
|
178
|
+
type: item[:type],
|
|
179
|
+
text: item[:text]
|
|
180
|
+
}
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
MCP::Tool::Response.new(content)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
private
|
|
187
|
+
|
|
188
|
+
def convert_schema(schema)
|
|
189
|
+
# If it's already a proper JSON schema, return it
|
|
190
|
+
if schema.is_a?(Hash) && schema[:type] && schema[:properties]
|
|
191
|
+
return schema
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Simple schema: hash mapping parameter names to types
|
|
195
|
+
if schema.is_a?(Hash)
|
|
196
|
+
properties = {}
|
|
197
|
+
schema.each do |param_name, param_type|
|
|
198
|
+
properties[param_name] = type_to_json_schema(param_type)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
type: 'object',
|
|
203
|
+
properties: properties,
|
|
204
|
+
required: properties.keys.map(&:to_s)
|
|
205
|
+
}
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Default fallback
|
|
209
|
+
{ type: 'object', properties: {} }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def type_to_json_schema(type)
|
|
213
|
+
case type
|
|
214
|
+
when :string, String
|
|
215
|
+
{ type: 'string' }
|
|
216
|
+
when :integer, Integer
|
|
217
|
+
{ type: 'integer' }
|
|
218
|
+
when :float, Float
|
|
219
|
+
{ type: 'number' }
|
|
220
|
+
when :boolean, TrueClass, FalseClass
|
|
221
|
+
{ type: 'boolean' }
|
|
222
|
+
when :number
|
|
223
|
+
{ type: 'number' }
|
|
224
|
+
else
|
|
225
|
+
{ type: 'string' } # Default fallback
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Create dynamic Resource classes from resource definitions
|
|
234
|
+
def create_resource_classes(resources)
|
|
235
|
+
resources.map do |resource_def|
|
|
236
|
+
# Create a new class that extends MCP::Resource
|
|
237
|
+
Class.new(MCP::Resource) do
|
|
238
|
+
@resource_def = resource_def
|
|
239
|
+
|
|
240
|
+
class << self
|
|
241
|
+
attr_reader :resource_def
|
|
242
|
+
|
|
243
|
+
def uri
|
|
244
|
+
@resource_def.uri
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def name
|
|
248
|
+
@resource_def.name
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def description
|
|
252
|
+
@resource_def.description
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def mime_type
|
|
256
|
+
@resource_def.mime_type
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def read
|
|
260
|
+
result = @resource_def.reader.call
|
|
261
|
+
|
|
262
|
+
# Convert to MCP format
|
|
263
|
+
result[:contents].map do |content|
|
|
264
|
+
MCP::ResourceContents.new(
|
|
265
|
+
uri: content[:uri],
|
|
266
|
+
mime_type: content[:mimeType] || content[:mime_type],
|
|
267
|
+
text: content[:text]
|
|
268
|
+
)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Create dynamic Prompt classes from prompt definitions
|
|
277
|
+
def create_prompt_classes(prompts)
|
|
278
|
+
prompts.map do |prompt_def|
|
|
279
|
+
# Create a new class that extends MCP::Prompt
|
|
280
|
+
Class.new(MCP::Prompt) do
|
|
281
|
+
@prompt_def = prompt_def
|
|
282
|
+
|
|
283
|
+
class << self
|
|
284
|
+
attr_reader :prompt_def
|
|
285
|
+
|
|
286
|
+
def name
|
|
287
|
+
@prompt_def.name
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def description
|
|
291
|
+
@prompt_def.description
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def arguments
|
|
295
|
+
@prompt_def.arguments || []
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def get(**args)
|
|
299
|
+
result = @prompt_def.generator.call(args)
|
|
300
|
+
|
|
301
|
+
# Convert to MCP format
|
|
302
|
+
{
|
|
303
|
+
messages: result[:messages].map do |msg|
|
|
304
|
+
{
|
|
305
|
+
role: msg[:role],
|
|
306
|
+
content: msg[:content]
|
|
307
|
+
}
|
|
308
|
+
end
|
|
309
|
+
}
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
123
316
|
def convert_input_schema(schema)
|
|
124
317
|
# If it's already a proper JSON schema, return it
|
|
125
318
|
if schema.is_a?(Hash) && schema[:type] && schema[:properties]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: claude-agent-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Community Contributors
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '2.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: mcp
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.4'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.4'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: bundler
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -114,7 +128,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
114
128
|
requirements:
|
|
115
129
|
- - ">="
|
|
116
130
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: 3.
|
|
131
|
+
version: 3.2.0
|
|
118
132
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
133
|
requirements:
|
|
120
134
|
- - ">="
|