robot_lab 0.0.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.
Files changed (153) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +1 -0
  3. data/.github/workflows/deploy-github-pages.yml +52 -0
  4. data/.github/workflows/deploy-yard-docs.yml +52 -0
  5. data/CHANGELOG.md +55 -0
  6. data/COMMITS.md +196 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +332 -0
  9. data/Rakefile +67 -0
  10. data/docs/api/adapters/anthropic.md +121 -0
  11. data/docs/api/adapters/gemini.md +133 -0
  12. data/docs/api/adapters/index.md +104 -0
  13. data/docs/api/adapters/openai.md +134 -0
  14. data/docs/api/core/index.md +113 -0
  15. data/docs/api/core/memory.md +314 -0
  16. data/docs/api/core/network.md +291 -0
  17. data/docs/api/core/robot.md +273 -0
  18. data/docs/api/core/state.md +273 -0
  19. data/docs/api/core/tool.md +353 -0
  20. data/docs/api/history/active-record-adapter.md +195 -0
  21. data/docs/api/history/config.md +191 -0
  22. data/docs/api/history/index.md +132 -0
  23. data/docs/api/history/thread-manager.md +144 -0
  24. data/docs/api/index.md +82 -0
  25. data/docs/api/mcp/client.md +221 -0
  26. data/docs/api/mcp/index.md +111 -0
  27. data/docs/api/mcp/server.md +225 -0
  28. data/docs/api/mcp/transports.md +264 -0
  29. data/docs/api/messages/index.md +67 -0
  30. data/docs/api/messages/text-message.md +102 -0
  31. data/docs/api/messages/tool-call-message.md +144 -0
  32. data/docs/api/messages/tool-result-message.md +154 -0
  33. data/docs/api/messages/user-message.md +171 -0
  34. data/docs/api/streaming/context.md +174 -0
  35. data/docs/api/streaming/events.md +237 -0
  36. data/docs/api/streaming/index.md +108 -0
  37. data/docs/architecture/core-concepts.md +243 -0
  38. data/docs/architecture/index.md +138 -0
  39. data/docs/architecture/message-flow.md +320 -0
  40. data/docs/architecture/network-orchestration.md +216 -0
  41. data/docs/architecture/robot-execution.md +243 -0
  42. data/docs/architecture/state-management.md +323 -0
  43. data/docs/assets/css/custom.css +56 -0
  44. data/docs/assets/images/robot_lab.jpg +0 -0
  45. data/docs/concepts.md +216 -0
  46. data/docs/examples/basic-chat.md +193 -0
  47. data/docs/examples/index.md +129 -0
  48. data/docs/examples/mcp-server.md +290 -0
  49. data/docs/examples/multi-robot-network.md +312 -0
  50. data/docs/examples/rails-application.md +420 -0
  51. data/docs/examples/tool-usage.md +310 -0
  52. data/docs/getting-started/configuration.md +230 -0
  53. data/docs/getting-started/index.md +56 -0
  54. data/docs/getting-started/installation.md +179 -0
  55. data/docs/getting-started/quick-start.md +203 -0
  56. data/docs/guides/building-robots.md +376 -0
  57. data/docs/guides/creating-networks.md +366 -0
  58. data/docs/guides/history.md +359 -0
  59. data/docs/guides/index.md +68 -0
  60. data/docs/guides/mcp-integration.md +356 -0
  61. data/docs/guides/memory.md +309 -0
  62. data/docs/guides/rails-integration.md +432 -0
  63. data/docs/guides/streaming.md +314 -0
  64. data/docs/guides/using-tools.md +394 -0
  65. data/docs/index.md +160 -0
  66. data/examples/01_simple_robot.rb +38 -0
  67. data/examples/02_tools.rb +106 -0
  68. data/examples/03_network.rb +103 -0
  69. data/examples/04_mcp.rb +219 -0
  70. data/examples/05_streaming.rb +124 -0
  71. data/examples/06_prompt_templates.rb +324 -0
  72. data/examples/07_network_memory.rb +329 -0
  73. data/examples/prompts/assistant/system.txt.erb +2 -0
  74. data/examples/prompts/assistant/user.txt.erb +1 -0
  75. data/examples/prompts/billing/system.txt.erb +7 -0
  76. data/examples/prompts/billing/user.txt.erb +1 -0
  77. data/examples/prompts/classifier/system.txt.erb +4 -0
  78. data/examples/prompts/classifier/user.txt.erb +1 -0
  79. data/examples/prompts/entity_extractor/system.txt.erb +11 -0
  80. data/examples/prompts/entity_extractor/user.txt.erb +3 -0
  81. data/examples/prompts/escalation/system.txt.erb +35 -0
  82. data/examples/prompts/escalation/user.txt.erb +34 -0
  83. data/examples/prompts/general/system.txt.erb +4 -0
  84. data/examples/prompts/general/user.txt.erb +1 -0
  85. data/examples/prompts/github_assistant/system.txt.erb +6 -0
  86. data/examples/prompts/github_assistant/user.txt.erb +1 -0
  87. data/examples/prompts/helper/system.txt.erb +1 -0
  88. data/examples/prompts/helper/user.txt.erb +1 -0
  89. data/examples/prompts/keyword_extractor/system.txt.erb +8 -0
  90. data/examples/prompts/keyword_extractor/user.txt.erb +3 -0
  91. data/examples/prompts/order_support/system.txt.erb +27 -0
  92. data/examples/prompts/order_support/user.txt.erb +22 -0
  93. data/examples/prompts/product_support/system.txt.erb +30 -0
  94. data/examples/prompts/product_support/user.txt.erb +32 -0
  95. data/examples/prompts/sentiment_analyzer/system.txt.erb +9 -0
  96. data/examples/prompts/sentiment_analyzer/user.txt.erb +3 -0
  97. data/examples/prompts/synthesizer/system.txt.erb +14 -0
  98. data/examples/prompts/synthesizer/user.txt.erb +15 -0
  99. data/examples/prompts/technical/system.txt.erb +7 -0
  100. data/examples/prompts/technical/user.txt.erb +1 -0
  101. data/examples/prompts/triage/system.txt.erb +16 -0
  102. data/examples/prompts/triage/user.txt.erb +17 -0
  103. data/lib/generators/robot_lab/install_generator.rb +78 -0
  104. data/lib/generators/robot_lab/robot_generator.rb +55 -0
  105. data/lib/generators/robot_lab/templates/initializer.rb.tt +41 -0
  106. data/lib/generators/robot_lab/templates/migration.rb.tt +32 -0
  107. data/lib/generators/robot_lab/templates/result_model.rb.tt +52 -0
  108. data/lib/generators/robot_lab/templates/robot.rb.tt +46 -0
  109. data/lib/generators/robot_lab/templates/robot_test.rb.tt +32 -0
  110. data/lib/generators/robot_lab/templates/routing_robot.rb.tt +53 -0
  111. data/lib/generators/robot_lab/templates/thread_model.rb.tt +40 -0
  112. data/lib/robot_lab/adapters/anthropic.rb +163 -0
  113. data/lib/robot_lab/adapters/base.rb +85 -0
  114. data/lib/robot_lab/adapters/gemini.rb +193 -0
  115. data/lib/robot_lab/adapters/openai.rb +159 -0
  116. data/lib/robot_lab/adapters/registry.rb +81 -0
  117. data/lib/robot_lab/configuration.rb +143 -0
  118. data/lib/robot_lab/error.rb +32 -0
  119. data/lib/robot_lab/errors.rb +70 -0
  120. data/lib/robot_lab/history/active_record_adapter.rb +146 -0
  121. data/lib/robot_lab/history/config.rb +115 -0
  122. data/lib/robot_lab/history/thread_manager.rb +93 -0
  123. data/lib/robot_lab/mcp/client.rb +210 -0
  124. data/lib/robot_lab/mcp/server.rb +84 -0
  125. data/lib/robot_lab/mcp/transports/base.rb +56 -0
  126. data/lib/robot_lab/mcp/transports/sse.rb +117 -0
  127. data/lib/robot_lab/mcp/transports/stdio.rb +133 -0
  128. data/lib/robot_lab/mcp/transports/streamable_http.rb +139 -0
  129. data/lib/robot_lab/mcp/transports/websocket.rb +108 -0
  130. data/lib/robot_lab/memory.rb +882 -0
  131. data/lib/robot_lab/memory_change.rb +123 -0
  132. data/lib/robot_lab/message.rb +357 -0
  133. data/lib/robot_lab/network.rb +350 -0
  134. data/lib/robot_lab/rails/engine.rb +29 -0
  135. data/lib/robot_lab/rails/railtie.rb +42 -0
  136. data/lib/robot_lab/robot.rb +560 -0
  137. data/lib/robot_lab/robot_result.rb +205 -0
  138. data/lib/robot_lab/robotic_model.rb +324 -0
  139. data/lib/robot_lab/state_proxy.rb +188 -0
  140. data/lib/robot_lab/streaming/context.rb +144 -0
  141. data/lib/robot_lab/streaming/events.rb +95 -0
  142. data/lib/robot_lab/streaming/sequence_counter.rb +48 -0
  143. data/lib/robot_lab/task.rb +117 -0
  144. data/lib/robot_lab/tool.rb +223 -0
  145. data/lib/robot_lab/tool_config.rb +112 -0
  146. data/lib/robot_lab/tool_manifest.rb +234 -0
  147. data/lib/robot_lab/user_message.rb +118 -0
  148. data/lib/robot_lab/version.rb +5 -0
  149. data/lib/robot_lab/waiter.rb +73 -0
  150. data/lib/robot_lab.rb +195 -0
  151. data/mkdocs.yml +214 -0
  152. data/sig/robot_lab.rbs +4 -0
  153. metadata +442 -0
@@ -0,0 +1,291 @@
1
+ # Network
2
+
3
+ Orchestrates multiple robots using SimpleFlow pipelines with DAG-based execution.
4
+
5
+ ## Class: `RobotLab::Network`
6
+
7
+ ```ruby
8
+ network = RobotLab.create_network(name: "support") do
9
+ task :classifier, classifier_robot, depends_on: :none
10
+ task :billing, billing_robot, depends_on: :optional
11
+ end
12
+ ```
13
+
14
+ ## Attributes
15
+
16
+ ### name
17
+
18
+ ```ruby
19
+ network.name # => String
20
+ ```
21
+
22
+ Network identifier for logging and debugging.
23
+
24
+ ### robots
25
+
26
+ ```ruby
27
+ network.robots # => Hash<String, Robot>
28
+ ```
29
+
30
+ Hash of robots keyed by name.
31
+
32
+ ### pipeline
33
+
34
+ ```ruby
35
+ network.pipeline # => SimpleFlow::Pipeline
36
+ ```
37
+
38
+ The underlying SimpleFlow pipeline.
39
+
40
+ ## Methods
41
+
42
+ ### run
43
+
44
+ ```ruby
45
+ result = network.run(
46
+ message: "Help me",
47
+ customer_id: 123,
48
+ **context
49
+ )
50
+ # => SimpleFlow::Result
51
+ ```
52
+
53
+ Execute the network pipeline.
54
+
55
+ **Parameters:**
56
+
57
+ | Name | Type | Description |
58
+ |------|------|-------------|
59
+ | `message` | `String` | The input message |
60
+ | `**context` | `Hash` | Additional context passed to all robots |
61
+
62
+ **Returns:** `SimpleFlow::Result`
63
+
64
+ ### task
65
+
66
+ ```ruby
67
+ network.task(name, robot, context: {}, mcp: :none, tools: :none, memory: nil, depends_on: :none)
68
+ # => self
69
+ ```
70
+
71
+ Add a task to the pipeline with optional per-task configuration.
72
+
73
+ **Parameters:**
74
+
75
+ | Name | Type | Description |
76
+ |------|------|-------------|
77
+ | `name` | `Symbol` | Task identifier |
78
+ | `robot` | `Robot` | Robot instance to execute |
79
+ | `context` | `Hash` | Task-specific context (deep-merged with run params) |
80
+ | `mcp` | `:none`, Array | MCP server config (`:none` or array of servers) |
81
+ | `tools` | `:none`, Array | Tools config (`:none` or array of tools) |
82
+ | `memory` | `Memory`, `nil` | Task-specific memory |
83
+ | `depends_on` | `:none`, `Array<Symbol>`, `:optional` | Task dependencies |
84
+
85
+ **Dependency Types:**
86
+
87
+ | Value | Description |
88
+ |-------|-------------|
89
+ | `:none` | No dependencies, runs first |
90
+ | `[:task1, :task2]` | Waits for listed tasks to complete |
91
+ | `:optional` | Only runs when explicitly activated |
92
+
93
+ ### add_robot
94
+
95
+ ```ruby
96
+ network.add_robot(robot)
97
+ # => self
98
+ ```
99
+
100
+ Add a robot without creating a pipeline task. Useful for robots referenced by other tasks.
101
+
102
+ ### robot / []
103
+
104
+ ```ruby
105
+ network.robot("billing") # => Robot
106
+ network["billing"] # => Robot (alias)
107
+ ```
108
+
109
+ Get robot by name.
110
+
111
+ ### available_robots
112
+
113
+ ```ruby
114
+ network.available_robots # => Array<Robot>
115
+ ```
116
+
117
+ Returns all robot instances.
118
+
119
+ ### visualize
120
+
121
+ ```ruby
122
+ network.visualize # => String
123
+ ```
124
+
125
+ ASCII visualization of the pipeline.
126
+
127
+ ### to_mermaid
128
+
129
+ ```ruby
130
+ network.to_mermaid # => String
131
+ ```
132
+
133
+ Mermaid diagram definition.
134
+
135
+ ### execution_plan
136
+
137
+ ```ruby
138
+ network.execution_plan # => String
139
+ ```
140
+
141
+ Human-readable execution plan.
142
+
143
+ ### to_h
144
+
145
+ ```ruby
146
+ network.to_h # => Hash
147
+ ```
148
+
149
+ Hash representation of network configuration.
150
+
151
+ ```ruby
152
+ {
153
+ name: "support",
154
+ robots: ["classifier", "billing", "technical"],
155
+ tasks: ["classifier", "billing", "technical"],
156
+ optional_tasks: [:billing, :technical]
157
+ }
158
+ ```
159
+
160
+ ## SimpleFlow::Result
161
+
162
+ When `run` is called, a `SimpleFlow::Result` is returned:
163
+
164
+ ### Attributes
165
+
166
+ ```ruby
167
+ result.value # Final task's output (RobotResult)
168
+ result.context # Hash of all task results
169
+ result.halted? # Whether execution stopped early
170
+ result.continued? # Whether execution continues
171
+ ```
172
+
173
+ ### Context Structure
174
+
175
+ ```ruby
176
+ result.context[:run_params] # Original run parameters
177
+ result.context[:classifier] # Classifier robot's RobotResult
178
+ result.context[:billing] # Billing robot's RobotResult (if activated)
179
+ ```
180
+
181
+ ## Builder DSL
182
+
183
+ ### task
184
+
185
+ ```ruby
186
+ network = RobotLab.create_network(name: "pipeline") do
187
+ task :first, robot1, depends_on: :none
188
+ task :second, robot2, depends_on: [:first]
189
+ task :optional, robot3, depends_on: :optional
190
+ end
191
+ ```
192
+
193
+ ### task with context
194
+
195
+ ```ruby
196
+ network = RobotLab.create_network(name: "support") do
197
+ task :classifier, classifier_robot, depends_on: :none
198
+ task :billing, billing_robot,
199
+ context: { department: "billing", escalation_level: 2 },
200
+ depends_on: :optional
201
+ task :technical, technical_robot,
202
+ context: { department: "technical" },
203
+ tools: [DebugTool, LogTool],
204
+ depends_on: :optional
205
+ end
206
+ ```
207
+
208
+ ## Examples
209
+
210
+ ### Sequential Pipeline
211
+
212
+ ```ruby
213
+ network = RobotLab.create_network(name: "pipeline") do
214
+ task :extract, extractor, depends_on: :none
215
+ task :transform, transformer, depends_on: [:extract]
216
+ task :load, loader, depends_on: [:transform]
217
+ end
218
+
219
+ result = network.run(message: "Process this document")
220
+ puts result.value.last_text_content
221
+ ```
222
+
223
+ ### Parallel Execution
224
+
225
+ ```ruby
226
+ network = RobotLab.create_network(name: "analysis", concurrency: :threads) do
227
+ task :fetch, fetcher, depends_on: :none
228
+
229
+ # Run in parallel
230
+ task :sentiment, sentiment_bot, depends_on: [:fetch]
231
+ task :entities, entity_bot, depends_on: [:fetch]
232
+
233
+ # Wait for both
234
+ task :merge, merger, depends_on: [:sentiment, :entities]
235
+ end
236
+ ```
237
+
238
+ ### Conditional Routing
239
+
240
+ ```ruby
241
+ class ClassifierRobot < RobotLab::Robot
242
+ def call(result)
243
+ robot_result = run(**extract_run_context(result))
244
+
245
+ new_result = result
246
+ .with_context(@name.to_sym, robot_result)
247
+ .continue(robot_result)
248
+
249
+ category = robot_result.last_text_content.to_s.downcase
250
+ case category
251
+ when /billing/ then new_result.activate(:billing)
252
+ when /technical/ then new_result.activate(:technical)
253
+ else new_result.activate(:general)
254
+ end
255
+ end
256
+ end
257
+
258
+ network = RobotLab.create_network(name: "support") do
259
+ task :classifier, ClassifierRobot.new(name: "classifier", template: :classifier),
260
+ depends_on: :none
261
+ task :billing, billing_robot, depends_on: :optional
262
+ task :technical, technical_robot, depends_on: :optional
263
+ task :general, general_robot, depends_on: :optional
264
+ end
265
+
266
+ result = network.run(message: "I have a billing question")
267
+ puts result.value.last_text_content
268
+ ```
269
+
270
+ ### Accessing Task Results
271
+
272
+ ```ruby
273
+ result = network.run(message: "Hello")
274
+
275
+ # Access individual task results
276
+ classifier_result = result.context[:classifier]
277
+ puts "Classification: #{classifier_result.last_text_content}"
278
+
279
+ # Check which optional task ran
280
+ if result.context[:billing]
281
+ puts "Billing handled the request"
282
+ elsif result.context[:technical]
283
+ puts "Technical handled the request"
284
+ end
285
+ ```
286
+
287
+ ## See Also
288
+
289
+ - [Creating Networks Guide](../../guides/creating-networks.md)
290
+ - [Network Orchestration](../../architecture/network-orchestration.md)
291
+ - [Robot](robot.md)
@@ -0,0 +1,273 @@
1
+ # Robot
2
+
3
+ LLM-powered agent with personality, tools, and model configuration.
4
+
5
+ ## Class: `RobotLab::Robot`
6
+
7
+ ```ruby
8
+ robot = RobotLab.build do
9
+ name "assistant"
10
+ description "A helpful assistant"
11
+ model "claude-sonnet-4"
12
+ template "You are helpful."
13
+ end
14
+ ```
15
+
16
+ ## Attributes
17
+
18
+ ### name
19
+
20
+ ```ruby
21
+ robot.name # => String
22
+ ```
23
+
24
+ Unique identifier for the robot within a network.
25
+
26
+ ### description
27
+
28
+ ```ruby
29
+ robot.description # => String
30
+ ```
31
+
32
+ Human-readable description of what the robot does.
33
+
34
+ ### model
35
+
36
+ ```ruby
37
+ robot.model # => String
38
+ ```
39
+
40
+ LLM model identifier (e.g., "claude-sonnet-4", "gpt-4o").
41
+
42
+ ### template
43
+
44
+ ```ruby
45
+ robot.template # => String
46
+ ```
47
+
48
+ System prompt that defines the robot's personality.
49
+
50
+ ### local_tools
51
+
52
+ ```ruby
53
+ robot.local_tools # => Array<Tool>
54
+ ```
55
+
56
+ Tools defined directly on the robot.
57
+
58
+ ### mcp_clients
59
+
60
+ ```ruby
61
+ robot.mcp_clients # => Array<MCP::Client>
62
+ ```
63
+
64
+ Connected MCP server clients.
65
+
66
+ ### mcp_tools
67
+
68
+ ```ruby
69
+ robot.mcp_tools # => Array<Tool>
70
+ ```
71
+
72
+ Tools discovered from MCP servers.
73
+
74
+ ### mcp_config
75
+
76
+ ```ruby
77
+ robot.mcp_config # => Symbol | Array
78
+ ```
79
+
80
+ MCP configuration (`:inherit`, `:none`, or server array).
81
+
82
+ ### tools_config
83
+
84
+ ```ruby
85
+ robot.tools_config # => Symbol | Array
86
+ ```
87
+
88
+ Tools whitelist configuration (`:inherit`, `:none`, or tool names).
89
+
90
+ ## Methods
91
+
92
+ ### tools
93
+
94
+ ```ruby
95
+ robot.tools # => Array<Tool>
96
+ ```
97
+
98
+ Returns all available tools (local + MCP, filtered by whitelist).
99
+
100
+ ### run
101
+
102
+ ```ruby
103
+ result = robot.run(
104
+ state: state,
105
+ network: network,
106
+ streaming: nil,
107
+ **context
108
+ )
109
+ # => RobotResult
110
+ ```
111
+
112
+ Execute the robot with the given state.
113
+
114
+ **Parameters:**
115
+
116
+ | Name | Type | Description |
117
+ |------|------|-------------|
118
+ | `state` | `State` | Conversation state |
119
+ | `network` | `Network`, `NetworkRun`, `nil` | Network context |
120
+ | `streaming` | `Proc`, `nil` | Streaming callback |
121
+ | `**context` | `Hash` | Additional context |
122
+
123
+ **Returns:** `RobotResult`
124
+
125
+ ### disconnect
126
+
127
+ ```ruby
128
+ robot.disconnect
129
+ ```
130
+
131
+ Disconnect from all MCP servers.
132
+
133
+ ### to_h
134
+
135
+ ```ruby
136
+ robot.to_h # => Hash
137
+ ```
138
+
139
+ Returns hash representation of the robot.
140
+
141
+ ## Builder DSL
142
+
143
+ ### name
144
+
145
+ ```ruby
146
+ name "my_robot"
147
+ ```
148
+
149
+ Set the robot's name.
150
+
151
+ ### description
152
+
153
+ ```ruby
154
+ description "Handles customer inquiries"
155
+ ```
156
+
157
+ Set the robot's description.
158
+
159
+ ### model
160
+
161
+ ```ruby
162
+ model "claude-sonnet-4"
163
+ ```
164
+
165
+ Set the LLM model.
166
+
167
+ ### template
168
+
169
+ ```ruby
170
+ # Inline template
171
+ template "You are a helpful assistant."
172
+
173
+ # Template file (loads from template_path)
174
+ template "support/system_prompt"
175
+
176
+ # Template file with variables
177
+ template "support/system_prompt", company: "Acme"
178
+ ```
179
+
180
+ Set the system prompt.
181
+
182
+ ### tool
183
+
184
+ ```ruby
185
+ tool :tool_name do
186
+ description "What the tool does"
187
+ parameter :param, type: :string, required: true
188
+ handler { |param:, **_| do_something(param) }
189
+ end
190
+ ```
191
+
192
+ Define a tool for the robot.
193
+
194
+ ### mcp
195
+
196
+ ```ruby
197
+ mcp :inherit # Use network's MCP servers
198
+ mcp :none # No MCP servers
199
+ mcp [ # Specific servers
200
+ { name: "fs", transport: { type: "stdio", command: "mcp-fs" } }
201
+ ]
202
+ ```
203
+
204
+ Configure MCP servers.
205
+
206
+ ### tools (whitelist)
207
+
208
+ ```ruby
209
+ tools :inherit # Use network's tools
210
+ tools :none # No inherited tools
211
+ tools %w[read_file write_file] # Only these tools
212
+ ```
213
+
214
+ Configure tool whitelist.
215
+
216
+ ## Examples
217
+
218
+ ### Basic Robot
219
+
220
+ ```ruby
221
+ robot = RobotLab.build do
222
+ name "greeter"
223
+ template "You greet users warmly."
224
+ end
225
+ ```
226
+
227
+ ### Robot with Tools
228
+
229
+ ```ruby
230
+ robot = RobotLab.build do
231
+ name "calculator"
232
+ model "claude-sonnet-4"
233
+ template "You help with math problems."
234
+
235
+ tool :add do
236
+ description "Add two numbers"
237
+ parameter :a, type: :number, required: true
238
+ parameter :b, type: :number, required: true
239
+ handler { |a:, b:, **_| a + b }
240
+ end
241
+
242
+ tool :multiply do
243
+ description "Multiply two numbers"
244
+ parameter :a, type: :number, required: true
245
+ parameter :b, type: :number, required: true
246
+ handler { |a:, b:, **_| a * b }
247
+ end
248
+ end
249
+ ```
250
+
251
+ ### Robot with MCP
252
+
253
+ ```ruby
254
+ robot = RobotLab.build do
255
+ name "developer"
256
+ template "You help with coding tasks."
257
+
258
+ mcp [
259
+ {
260
+ name: "github",
261
+ transport: { type: "stdio", command: "mcp-server-github" }
262
+ }
263
+ ]
264
+
265
+ tools %w[search_repositories create_issue]
266
+ end
267
+ ```
268
+
269
+ ## See Also
270
+
271
+ - [Building Robots Guide](../../guides/building-robots.md)
272
+ - [Tool](tool.md)
273
+ - [Network](network.md)