claude_agent 0.7.14 → 0.7.16

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/rules/conventions.md +66 -16
  3. data/CHANGELOG.md +20 -0
  4. data/CLAUDE.md +24 -4
  5. data/README.md +52 -1529
  6. data/SPEC.md +56 -29
  7. data/docs/architecture.md +339 -0
  8. data/docs/client.md +526 -0
  9. data/docs/configuration.md +571 -0
  10. data/docs/conversations.md +461 -0
  11. data/docs/errors.md +127 -0
  12. data/docs/events.md +225 -0
  13. data/docs/getting-started.md +310 -0
  14. data/docs/hooks.md +380 -0
  15. data/docs/logging.md +96 -0
  16. data/docs/mcp.md +308 -0
  17. data/docs/messages.md +871 -0
  18. data/docs/permissions.md +611 -0
  19. data/docs/queries.md +227 -0
  20. data/docs/sessions.md +335 -0
  21. data/lib/claude_agent/abort_controller.rb +24 -0
  22. data/lib/claude_agent/client/commands.rb +32 -0
  23. data/lib/claude_agent/client.rb +10 -4
  24. data/lib/claude_agent/configuration.rb +129 -0
  25. data/lib/claude_agent/control_protocol/commands.rb +28 -0
  26. data/lib/claude_agent/conversation.rb +37 -4
  27. data/lib/claude_agent/errors.rb +21 -4
  28. data/lib/claude_agent/event_handler.rb +14 -0
  29. data/lib/claude_agent/fork_session.rb +117 -0
  30. data/lib/claude_agent/hook_registry.rb +110 -0
  31. data/lib/claude_agent/hooks.rb +4 -0
  32. data/lib/claude_agent/mcp/server.rb +22 -0
  33. data/lib/claude_agent/mcp/tool.rb +24 -3
  34. data/lib/claude_agent/message.rb +93 -0
  35. data/lib/claude_agent/messages/streaming.rb +37 -0
  36. data/lib/claude_agent/options.rb +10 -0
  37. data/lib/claude_agent/permission_policy.rb +174 -0
  38. data/lib/claude_agent/permission_request.rb +17 -0
  39. data/lib/claude_agent/session.rb +100 -11
  40. data/lib/claude_agent/session_paths.rb +5 -2
  41. data/lib/claude_agent/turn_result.rb +20 -2
  42. data/lib/claude_agent/types/sessions.rb +8 -0
  43. data/lib/claude_agent/version.rb +1 -1
  44. data/lib/claude_agent.rb +187 -0
  45. data/sig/claude_agent.rbs +38 -1
  46. metadata +20 -1
data/docs/mcp.md ADDED
@@ -0,0 +1,308 @@
1
+ # MCP Tools
2
+
3
+ The ClaudeAgent Ruby SDK lets you define MCP (Model Context Protocol) tool servers that run in-process -- in the same Ruby process as your application. Unlike external MCP servers that launch as subprocesses communicating over stdio, SDK servers handle tool calls directly via method dispatch. This means faster execution, shared state with your application, and straightforward debugging.
4
+
5
+ ## Block DSL (Recommended)
6
+
7
+ The most concise way to define an MCP server is the block DSL. Pass a block to `Server.new` and call `tool` to register each tool inline:
8
+
9
+ ```ruby
10
+ server = ClaudeAgent::MCP::Server.new(name: "calculator") do |s|
11
+ s.tool("add", "Add two numbers", { a: :number, b: :number }) do |args|
12
+ (args[:a] + args[:b]).to_s
13
+ end
14
+
15
+ s.tool("multiply", "Multiply two numbers", { a: :number, b: :number }) do |args|
16
+ (args[:a] * args[:b]).to_s
17
+ end
18
+ end
19
+ ```
20
+
21
+ Each call to `s.tool(name, description, schema, &handler)` creates a `ClaudeAgent::MCP::Tool` and registers it on the server. The handler block receives a **symbol-keyed** Hash of the arguments Claude provides.
22
+
23
+ ## Traditional Approach
24
+
25
+ Create `Tool` objects first, then pass them to the server constructor:
26
+
27
+ ```ruby
28
+ add_tool = ClaudeAgent::MCP::Tool.new(
29
+ name: "add",
30
+ description: "Add two numbers",
31
+ schema: { a: Float, b: Float }
32
+ ) { |args| (args[:a] + args[:b]).to_s }
33
+
34
+ subtract_tool = ClaudeAgent::MCP::Tool.new(
35
+ name: "subtract",
36
+ description: "Subtract two numbers",
37
+ schema: { a: Float, b: Float }
38
+ ) { |args| (args[:a] - args[:b]).to_s }
39
+
40
+ server = ClaudeAgent::MCP::Server.new(
41
+ name: "calculator",
42
+ tools: [add_tool, subtract_tool]
43
+ )
44
+ ```
45
+
46
+ You can also add and remove tools after construction:
47
+
48
+ ```ruby
49
+ server.add_tool(new_tool)
50
+ server.remove_tool("old_tool")
51
+ ```
52
+
53
+ ## Convenience Methods
54
+
55
+ `ClaudeAgent::MCP` provides module-level shortcuts for creating tools and servers without fully qualifying the class names:
56
+
57
+ ```ruby
58
+ tool = ClaudeAgent::MCP.tool("greet", "Greet someone", { name: String }) do |args|
59
+ "Hello, #{args[:name]}!"
60
+ end
61
+
62
+ server = ClaudeAgent::MCP.create_server(name: "greeter", tools: [tool])
63
+ ```
64
+
65
+ ## Tool Schema
66
+
67
+ The schema defines the JSON Schema for the tool's input parameters. Three formats are supported.
68
+
69
+ ### Ruby class mapping
70
+
71
+ Pass a Hash mapping parameter names to Ruby classes. All parameters are marked as required:
72
+
73
+ ```ruby
74
+ schema: { name: String, age: Integer, score: Float }
75
+ ```
76
+
77
+ | Ruby Class | JSON Schema type |
78
+ |---------------------------|------------------|
79
+ | `String` | `string` |
80
+ | `Integer` | `integer` |
81
+ | `Float`, `Numeric` | `number` |
82
+ | `TrueClass`, `FalseClass` | `boolean` |
83
+ | `Array` | `array` |
84
+ | `Hash` | `object` |
85
+
86
+ ### Symbol shortcuts
87
+
88
+ Use symbols instead of classes for a more compact definition:
89
+
90
+ ```ruby
91
+ schema: { name: :string, count: :integer, ratio: :number, active: :boolean }
92
+ ```
93
+
94
+ | Symbol | JSON Schema type |
95
+ |---------------------------------|------------------|
96
+ | `:string`, `:str` | `string` |
97
+ | `:integer`, `:int` | `integer` |
98
+ | `:number`, `:float`, `:numeric` | `number` |
99
+ | `:boolean`, `:bool` | `boolean` |
100
+ | `:array` | `array` |
101
+ | `:object`, `:hash` | `object` |
102
+
103
+ ### Raw JSON Schema
104
+
105
+ For full control (enums, optional fields, nested objects), pass a standard JSON Schema Hash directly. The SDK detects raw schemas by the presence of a `type` or `properties` key and passes them through unchanged:
106
+
107
+ ```ruby
108
+ schema: {
109
+ type: "object",
110
+ properties: {
111
+ operation: { type: "string", enum: ["add", "subtract", "multiply"] },
112
+ a: { type: "number" },
113
+ b: { type: "number" }
114
+ },
115
+ required: ["operation", "a", "b"]
116
+ }
117
+ ```
118
+
119
+ ## Tool Annotations
120
+
121
+ MCP tool annotations provide hints to the model about tool behavior. Pass an `annotations` Hash when creating a tool:
122
+
123
+ ```ruby
124
+ server = ClaudeAgent::MCP::Server.new(name: "files") do |s|
125
+ s.tool("read_file", "Read a file", { path: :string },
126
+ annotations: {
127
+ readOnlyHint: true,
128
+ destructiveHint: false,
129
+ idempotentHint: true,
130
+ openWorldHint: false,
131
+ title: "Read File"
132
+ }
133
+ ) do |args|
134
+ File.read(args[:path])
135
+ end
136
+ end
137
+ ```
138
+
139
+ | Annotation | Type | Description |
140
+ |-------------------|---------|---------------------------------------------------|
141
+ | `readOnlyHint` | Boolean | Tool does not modify state |
142
+ | `destructiveHint` | Boolean | Tool may perform destructive operations |
143
+ | `idempotentHint` | Boolean | Repeated calls with same args produce same result |
144
+ | `openWorldHint` | Boolean | Tool interacts with external systems |
145
+ | `title` | String | Human-readable display name |
146
+
147
+ Annotations are omitted from the MCP definition when `nil` or empty.
148
+
149
+ ## Tool Return Values
150
+
151
+ The handler block's return value is automatically formatted into the MCP response structure. Several return types are supported:
152
+
153
+ **String** -- wrapped in a text content block:
154
+
155
+ ```ruby
156
+ s.tool("greet", "Greet", { name: :string }) do |args|
157
+ "Hello, #{args[:name]}!"
158
+ end
159
+ # => { content: [{ type: "text", text: "Hello, World!" }], isError: false }
160
+ ```
161
+
162
+ **Numeric or other non-String** -- converted via `to_s` and wrapped in a text content block:
163
+
164
+ ```ruby
165
+ s.tool("add", "Add", { a: :number, b: :number }) do |args|
166
+ args[:a] + args[:b]
167
+ end
168
+ # => { content: [{ type: "text", text: "7.5" }], isError: false }
169
+ ```
170
+
171
+ **Hash with `:content` key** -- used as-is, letting you return custom content blocks:
172
+
173
+ ```ruby
174
+ s.tool("image", "Generate image", {}) do |args|
175
+ { content: [{ type: "image", data: base64_data, mimeType: "image/png" }] }
176
+ end
177
+ ```
178
+
179
+ **Array** -- treated as a pre-built content block array:
180
+
181
+ ```ruby
182
+ s.tool("multi", "Multiple outputs", {}) do |args|
183
+ [
184
+ { type: "text", text: "Part 1" },
185
+ { type: "text", text: "Part 2" }
186
+ ]
187
+ end
188
+ ```
189
+
190
+ **Exceptions** -- if the handler raises, the error is caught and returned as an error response:
191
+
192
+ ```ruby
193
+ s.tool("divide", "Divide", { a: :number, b: :number }) do |args|
194
+ raise "Division by zero" if args[:b] == 0
195
+ (args[:a] / args[:b]).to_s
196
+ end
197
+ # When b=0: { content: [{ type: "text", text: "Error: Division by zero" }], isError: true }
198
+ ```
199
+
200
+ ## Using with Options
201
+
202
+ To attach an MCP server to a query, pass it via the `mcp_servers` option. Each server entry is keyed by name and must include `type: "sdk"` and `instance:` pointing to the server object:
203
+
204
+ ```ruby
205
+ server = ClaudeAgent::MCP::Server.new(name: "calculator") do |s|
206
+ s.tool("add", "Add two numbers", { a: :number, b: :number }) do |args|
207
+ (args[:a] + args[:b]).to_s
208
+ end
209
+ end
210
+
211
+ options = ClaudeAgent::Options.new(
212
+ mcp_servers: {
213
+ "calculator" => { type: "sdk", instance: server }
214
+ }
215
+ )
216
+
217
+ turn = ClaudeAgent.ask("What is 2 + 3?", options: options)
218
+ ```
219
+
220
+ The server also provides a `to_config` shortcut that builds the config Hash for you:
221
+
222
+ ```ruby
223
+ options = ClaudeAgent::Options.new(
224
+ mcp_servers: {
225
+ "calculator" => server.to_config
226
+ }
227
+ )
228
+ ```
229
+
230
+ ## Global Registration
231
+
232
+ Register an MCP server globally so it is available to all queries without passing it in every `Options`:
233
+
234
+ ```ruby
235
+ server = ClaudeAgent::MCP::Server.new(name: "calculator") do |s|
236
+ s.tool("add", "Add two numbers", { a: :number, b: :number }) do |args|
237
+ (args[:a] + args[:b]).to_s
238
+ end
239
+ end
240
+
241
+ ClaudeAgent.register_mcp_server(server)
242
+
243
+ # Now every query can use the calculator tools
244
+ turn = ClaudeAgent.ask("What is 2 + 3?")
245
+ ```
246
+
247
+ Globally registered servers are stored in `ClaudeAgent.config.default_mcp_servers` and merged into the effective `Options` for each request.
248
+
249
+ ## External MCP Servers
250
+
251
+ In addition to in-process SDK servers, you can configure external MCP servers that run as subprocesses. These use the `stdio` transport type and are passed directly to the Claude Code CLI:
252
+
253
+ ```ruby
254
+ options = ClaudeAgent::Options.new(
255
+ mcp_servers: {
256
+ "filesystem" => {
257
+ type: "stdio",
258
+ command: "npx",
259
+ args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
260
+ },
261
+ "database" => {
262
+ type: "stdio",
263
+ command: "python",
264
+ args: ["-m", "mcp_server_sqlite", "--db", "app.db"]
265
+ }
266
+ }
267
+ )
268
+ ```
269
+
270
+ You can mix SDK and external servers in the same `mcp_servers` Hash. The SDK filters out external servers and passes their configuration to the CLI's `--mcp-config` flag, while SDK servers are handled in-process.
271
+
272
+ ## MCP Elicitation
273
+
274
+ MCP elicitation allows an MCP server to request additional input from the user (or your application) during a tool call -- for example, to handle OAuth consent or collect form data.
275
+
276
+ Set the `on_elicitation` callback in your `Options`. The callback receives a request Hash and must return a response Hash:
277
+
278
+ ```ruby
279
+ options = ClaudeAgent::Options.new(
280
+ on_elicitation: ->(request, signal:) {
281
+ # request keys:
282
+ # :server_name - which MCP server triggered this
283
+ # :message - display message from the server
284
+ # :mode - elicitation mode
285
+ # :url - URL for OAuth flows (if applicable)
286
+ # :elicitation_id - unique ID for this elicitation
287
+ # :requested_schema - schema describing expected input
288
+
289
+ case request[:mode]
290
+ when "oauth"
291
+ # Handle OAuth flow, return approval
292
+ { action: "approve", content: { token: "..." } }
293
+ else
294
+ # Decline unknown elicitation types
295
+ { action: "decline" }
296
+ end
297
+ }
298
+ )
299
+ ```
300
+
301
+ The callback must return a Hash with an `action` key. Supported actions:
302
+
303
+ | Action | Description |
304
+ |-------------|--------------------------------------------------------------------|
305
+ | `"approve"` | Accept the elicitation and optionally provide `content` |
306
+ | `"decline"` | Reject the elicitation (this is the default if no callback is set) |
307
+
308
+ If no `on_elicitation` callback is configured, all elicitation requests are declined automatically.