robot_lab 0.0.1 → 0.0.6
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/.github/workflows/deploy-github-pages.yml +9 -9
- data/.irbrc +6 -0
- data/CHANGELOG.md +140 -0
- data/README.md +263 -48
- data/Rakefile +71 -1
- data/docs/api/core/index.md +53 -46
- data/docs/api/core/memory.md +200 -154
- data/docs/api/core/network.md +13 -3
- data/docs/api/core/robot.md +490 -130
- data/docs/api/core/state.md +55 -73
- data/docs/api/core/tool.md +205 -209
- data/docs/api/index.md +7 -28
- data/docs/api/mcp/client.md +119 -48
- data/docs/api/mcp/index.md +75 -60
- data/docs/api/mcp/server.md +120 -136
- data/docs/api/mcp/transports.md +172 -184
- data/docs/api/messages/index.md +35 -20
- data/docs/api/messages/text-message.md +67 -21
- data/docs/api/messages/tool-call-message.md +80 -41
- data/docs/api/messages/tool-result-message.md +119 -50
- data/docs/api/messages/user-message.md +48 -24
- data/docs/api/streaming/context.md +157 -74
- data/docs/api/streaming/events.md +114 -166
- data/docs/api/streaming/index.md +74 -72
- data/docs/architecture/core-concepts.md +360 -116
- data/docs/architecture/index.md +97 -59
- data/docs/architecture/message-flow.md +138 -129
- data/docs/architecture/network-orchestration.md +197 -50
- data/docs/architecture/robot-execution.md +199 -146
- data/docs/architecture/state-management.md +255 -187
- data/docs/concepts.md +311 -49
- data/docs/examples/basic-chat.md +89 -77
- data/docs/examples/index.md +222 -47
- data/docs/examples/mcp-server.md +207 -203
- data/docs/examples/multi-robot-network.md +129 -35
- data/docs/examples/rails-application.md +159 -160
- data/docs/examples/tool-usage.md +295 -204
- data/docs/getting-started/configuration.md +347 -154
- data/docs/getting-started/index.md +1 -1
- data/docs/getting-started/installation.md +22 -13
- data/docs/getting-started/quick-start.md +166 -121
- data/docs/guides/building-robots.md +418 -212
- data/docs/guides/creating-networks.md +143 -24
- data/docs/guides/index.md +0 -5
- data/docs/guides/mcp-integration.md +152 -113
- data/docs/guides/memory.md +220 -164
- data/docs/guides/rails-integration.md +244 -162
- data/docs/guides/streaming.md +137 -187
- data/docs/guides/using-tools.md +259 -212
- data/docs/index.md +46 -41
- data/examples/01_simple_robot.rb +6 -9
- data/examples/02_tools.rb +6 -9
- data/examples/03_network.rb +19 -17
- data/examples/04_mcp.rb +5 -8
- data/examples/05_streaming.rb +5 -8
- data/examples/06_prompt_templates.rb +42 -37
- data/examples/07_network_memory.rb +13 -14
- data/examples/08_llm_config.rb +169 -0
- data/examples/09_chaining.rb +262 -0
- data/examples/10_memory.rb +331 -0
- data/examples/11_network_introspection.rb +253 -0
- data/examples/12_message_bus.rb +74 -0
- data/examples/13_spawn.rb +90 -0
- data/examples/14_rusty_circuit/comic.rb +143 -0
- data/examples/14_rusty_circuit/display.rb +203 -0
- data/examples/14_rusty_circuit/heckler.rb +63 -0
- data/examples/14_rusty_circuit/open_mic.rb +123 -0
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +20 -0
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +23 -0
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +20 -0
- data/examples/14_rusty_circuit/scout.rb +156 -0
- data/examples/14_rusty_circuit/scout_notes.md +89 -0
- data/examples/14_rusty_circuit/show.log +234 -0
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +24 -0
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +206 -0
- data/examples/15_memory_network_and_bus/linux_writer.rb +80 -0
- data/examples/15_memory_network_and_bus/os_editor.rb +46 -0
- data/examples/15_memory_network_and_bus/os_writer.rb +46 -0
- data/examples/15_memory_network_and_bus/output/combined_article.md +13 -0
- data/examples/15_memory_network_and_bus/output/final_article.md +15 -0
- data/examples/15_memory_network_and_bus/output/linux_draft.md +5 -0
- data/examples/15_memory_network_and_bus/output/mac_draft.md +7 -0
- data/examples/15_memory_network_and_bus/output/memory.json +13 -0
- data/examples/15_memory_network_and_bus/output/revision_1.md +19 -0
- data/examples/15_memory_network_and_bus/output/revision_2.md +15 -0
- data/examples/15_memory_network_and_bus/output/windows_draft.md +7 -0
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +13 -0
- data/examples/16_writers_room/display.rb +158 -0
- data/examples/16_writers_room/output/.gitignore +2 -0
- data/examples/16_writers_room/output/opus_001.md +263 -0
- data/examples/16_writers_room/output/opus_001_notes.log +470 -0
- data/examples/16_writers_room/prompts/writer.md +37 -0
- data/examples/16_writers_room/room.rb +150 -0
- data/examples/16_writers_room/tools.rb +162 -0
- data/examples/16_writers_room/writer.rb +121 -0
- data/examples/16_writers_room/writers_room.rb +162 -0
- data/examples/README.md +197 -0
- data/examples/prompts/{assistant/system.txt.erb → assistant.md} +3 -0
- data/examples/prompts/{billing/system.txt.erb → billing.md} +3 -0
- data/examples/prompts/{classifier/system.txt.erb → classifier.md} +3 -0
- data/examples/prompts/comedian.md +6 -0
- data/examples/prompts/comedy_critic.md +10 -0
- data/examples/prompts/configurable.md +9 -0
- data/examples/prompts/dispatcher.md +12 -0
- data/examples/prompts/{entity_extractor/system.txt.erb → entity_extractor.md} +3 -0
- data/examples/prompts/{escalation/system.txt.erb → escalation.md} +7 -0
- data/examples/prompts/frontmatter_mcp_test.md +9 -0
- data/examples/prompts/frontmatter_named_test.md +5 -0
- data/examples/prompts/frontmatter_tools_test.md +6 -0
- data/examples/prompts/{general/system.txt.erb → general.md} +3 -0
- data/examples/prompts/{github_assistant/system.txt.erb → github_assistant.md} +8 -0
- data/examples/prompts/{helper/system.txt.erb → helper.md} +3 -0
- data/examples/prompts/{keyword_extractor/system.txt.erb → keyword_extractor.md} +3 -0
- data/examples/prompts/llm_config_demo.md +20 -0
- data/examples/prompts/{order_support/system.txt.erb → order_support.md} +8 -0
- data/examples/prompts/os_advocate.md +13 -0
- data/examples/prompts/os_chief.md +13 -0
- data/examples/prompts/os_editor.md +13 -0
- data/examples/prompts/{product_support/system.txt.erb → product_support.md} +7 -0
- data/examples/prompts/{sentiment_analyzer/system.txt.erb → sentiment_analyzer.md} +3 -0
- data/examples/prompts/{synthesizer/system.txt.erb → synthesizer.md} +3 -0
- data/examples/prompts/{technical/system.txt.erb → technical.md} +3 -0
- data/examples/prompts/{triage/system.txt.erb → triage.md} +6 -0
- data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -13
- data/lib/robot_lab/ask_user.rb +75 -0
- data/lib/robot_lab/config/defaults.yml +121 -0
- data/lib/robot_lab/config.rb +183 -0
- data/lib/robot_lab/error.rb +6 -0
- data/lib/robot_lab/mcp/client.rb +1 -1
- data/lib/robot_lab/memory.rb +10 -34
- data/lib/robot_lab/network.rb +13 -20
- data/lib/robot_lab/robot/bus_messaging.rb +239 -0
- data/lib/robot_lab/robot/mcp_management.rb +88 -0
- data/lib/robot_lab/robot/template_rendering.rb +130 -0
- data/lib/robot_lab/robot.rb +240 -330
- data/lib/robot_lab/robot_message.rb +44 -0
- data/lib/robot_lab/robot_result.rb +1 -0
- data/lib/robot_lab/run_config.rb +184 -0
- data/lib/robot_lab/state_proxy.rb +2 -12
- data/lib/robot_lab/streaming/context.rb +1 -1
- data/lib/robot_lab/task.rb +8 -1
- data/lib/robot_lab/tool.rb +108 -172
- data/lib/robot_lab/tool_config.rb +1 -1
- data/lib/robot_lab/tool_manifest.rb +2 -18
- data/lib/robot_lab/utils.rb +39 -0
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab.rb +89 -57
- data/mkdocs.yml +0 -11
- metadata +121 -135
- data/docs/api/adapters/anthropic.md +0 -121
- data/docs/api/adapters/gemini.md +0 -133
- data/docs/api/adapters/index.md +0 -104
- data/docs/api/adapters/openai.md +0 -134
- data/docs/api/history/active-record-adapter.md +0 -195
- data/docs/api/history/config.md +0 -191
- data/docs/api/history/index.md +0 -132
- data/docs/api/history/thread-manager.md +0 -144
- data/docs/guides/history.md +0 -359
- data/examples/prompts/assistant/user.txt.erb +0 -1
- data/examples/prompts/billing/user.txt.erb +0 -1
- data/examples/prompts/classifier/user.txt.erb +0 -1
- data/examples/prompts/entity_extractor/user.txt.erb +0 -3
- data/examples/prompts/escalation/user.txt.erb +0 -34
- data/examples/prompts/general/user.txt.erb +0 -1
- data/examples/prompts/github_assistant/user.txt.erb +0 -1
- data/examples/prompts/helper/user.txt.erb +0 -1
- data/examples/prompts/keyword_extractor/user.txt.erb +0 -3
- data/examples/prompts/order_support/user.txt.erb +0 -22
- data/examples/prompts/product_support/user.txt.erb +0 -32
- data/examples/prompts/sentiment_analyzer/user.txt.erb +0 -3
- data/examples/prompts/synthesizer/user.txt.erb +0 -15
- data/examples/prompts/technical/user.txt.erb +0 -1
- data/examples/prompts/triage/user.txt.erb +0 -17
- data/lib/robot_lab/adapters/anthropic.rb +0 -163
- data/lib/robot_lab/adapters/base.rb +0 -85
- data/lib/robot_lab/adapters/gemini.rb +0 -193
- data/lib/robot_lab/adapters/openai.rb +0 -159
- data/lib/robot_lab/adapters/registry.rb +0 -81
- data/lib/robot_lab/configuration.rb +0 -143
- data/lib/robot_lab/errors.rb +0 -70
- data/lib/robot_lab/history/active_record_adapter.rb +0 -146
- data/lib/robot_lab/history/config.rb +0 -115
- data/lib/robot_lab/history/thread_manager.rb +0 -93
- data/lib/robot_lab/robotic_model.rb +0 -324
|
@@ -38,6 +38,20 @@ network = RobotLab.create_network(name: "parallel", concurrency: :threads) do
|
|
|
38
38
|
end
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
### Shared Memory
|
|
42
|
+
|
|
43
|
+
Networks provide a shared memory accessible to all robots:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
network = RobotLab.create_network(name: "pipeline") do
|
|
47
|
+
task :first, robot1, depends_on: :none
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Pre-populate shared memory
|
|
51
|
+
network.memory[:project] = "Q4 Report"
|
|
52
|
+
network.memory[:user_id] = 123
|
|
53
|
+
```
|
|
54
|
+
|
|
41
55
|
## Adding Tasks
|
|
42
56
|
|
|
43
57
|
### Sequential Tasks
|
|
@@ -72,7 +86,7 @@ end
|
|
|
72
86
|
|
|
73
87
|
### Optional Tasks
|
|
74
88
|
|
|
75
|
-
Optional tasks only run when explicitly activated:
|
|
89
|
+
Optional tasks only run when explicitly activated by a preceding robot:
|
|
76
90
|
|
|
77
91
|
```ruby
|
|
78
92
|
network = RobotLab.create_network(name: "router") do
|
|
@@ -85,7 +99,7 @@ end
|
|
|
85
99
|
|
|
86
100
|
## Per-Task Configuration
|
|
87
101
|
|
|
88
|
-
Tasks can have individual context and configuration that
|
|
102
|
+
Tasks can have individual context and configuration that is deep-merged with the network's run parameters:
|
|
89
103
|
|
|
90
104
|
```ruby
|
|
91
105
|
network = RobotLab.create_network(name: "support") do
|
|
@@ -105,9 +119,10 @@ end
|
|
|
105
119
|
| Option | Description |
|
|
106
120
|
|--------|-------------|
|
|
107
121
|
| `context` | Hash merged with run params (task values override) |
|
|
108
|
-
| `mcp` | MCP servers for this task |
|
|
109
|
-
| `tools` | Tools available to this task |
|
|
122
|
+
| `mcp` | MCP servers for this task (`:none`, `:inherit`, or array) |
|
|
123
|
+
| `tools` | Tools available to this task (`:none`, `:inherit`, or array) |
|
|
110
124
|
| `memory` | Task-specific memory |
|
|
125
|
+
| `config` | Per-task `RunConfig` (merged on top of network's config) |
|
|
111
126
|
| `depends_on` | `:none`, `[:task1]`, or `:optional` |
|
|
112
127
|
|
|
113
128
|
## Conditional Routing
|
|
@@ -117,7 +132,9 @@ Use optional tasks with custom Robot subclasses for intelligent routing:
|
|
|
117
132
|
```ruby
|
|
118
133
|
class ClassifierRobot < RobotLab::Robot
|
|
119
134
|
def call(result)
|
|
120
|
-
|
|
135
|
+
context = extract_run_context(result)
|
|
136
|
+
message = context.delete(:message)
|
|
137
|
+
robot_result = run(message, **context)
|
|
121
138
|
|
|
122
139
|
new_result = result
|
|
123
140
|
.with_context(@name.to_sym, robot_result)
|
|
@@ -194,6 +211,25 @@ result.halted? # Whether execution was halted early
|
|
|
194
211
|
result.continued? # Whether execution continued normally
|
|
195
212
|
```
|
|
196
213
|
|
|
214
|
+
## Broadcasting
|
|
215
|
+
|
|
216
|
+
Networks support a broadcast channel for network-wide announcements:
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
# Register a broadcast handler
|
|
220
|
+
network.on_broadcast do |message|
|
|
221
|
+
case message[:payload][:event]
|
|
222
|
+
when :pause
|
|
223
|
+
puts "Pausing: #{message[:payload][:reason]}"
|
|
224
|
+
when :phase_complete
|
|
225
|
+
puts "Phase complete: #{message[:payload][:phase]}"
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Send broadcasts during execution
|
|
230
|
+
network.broadcast(event: :phase_complete, phase: "analysis")
|
|
231
|
+
```
|
|
232
|
+
|
|
197
233
|
## Patterns
|
|
198
234
|
|
|
199
235
|
### Classifier Pattern
|
|
@@ -203,7 +239,10 @@ Route to specialists based on classification:
|
|
|
203
239
|
```ruby
|
|
204
240
|
class SupportClassifier < RobotLab::Robot
|
|
205
241
|
def call(result)
|
|
206
|
-
|
|
242
|
+
context = extract_run_context(result)
|
|
243
|
+
message = context.delete(:message)
|
|
244
|
+
robot_result = run(message, **context)
|
|
245
|
+
|
|
207
246
|
new_result = result
|
|
208
247
|
.with_context(@name.to_sym, robot_result)
|
|
209
248
|
.continue(robot_result)
|
|
@@ -259,7 +298,9 @@ A robot can halt execution early:
|
|
|
259
298
|
```ruby
|
|
260
299
|
class ValidatorRobot < RobotLab::Robot
|
|
261
300
|
def call(result)
|
|
262
|
-
|
|
301
|
+
context = extract_run_context(result)
|
|
302
|
+
message = context.delete(:message)
|
|
303
|
+
robot_result = run(message, **context)
|
|
263
304
|
|
|
264
305
|
if robot_result.last_text_content.include?("INVALID")
|
|
265
306
|
# Stop the pipeline
|
|
@@ -274,6 +315,30 @@ class ValidatorRobot < RobotLab::Robot
|
|
|
274
315
|
end
|
|
275
316
|
```
|
|
276
317
|
|
|
318
|
+
### Data Passing Between Tasks
|
|
319
|
+
|
|
320
|
+
Access previous task results via context:
|
|
321
|
+
|
|
322
|
+
```ruby
|
|
323
|
+
class ResponderRobot < RobotLab::Robot
|
|
324
|
+
def call(result)
|
|
325
|
+
# Get classifier's output
|
|
326
|
+
classification = result.context[:classifier]&.last_text_content
|
|
327
|
+
|
|
328
|
+
context = extract_run_context(result)
|
|
329
|
+
message = context.delete(:message)
|
|
330
|
+
|
|
331
|
+
# Use classification in the message or context
|
|
332
|
+
robot_result = run(
|
|
333
|
+
"Classification: #{classification}\n\nUser message: #{message}",
|
|
334
|
+
**context
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
result.with_context(@name.to_sym, robot_result).continue(robot_result)
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
```
|
|
341
|
+
|
|
277
342
|
## Visualization
|
|
278
343
|
|
|
279
344
|
### ASCII Visualization
|
|
@@ -290,6 +355,13 @@ puts network.to_mermaid
|
|
|
290
355
|
# => Mermaid graph definition
|
|
291
356
|
```
|
|
292
357
|
|
|
358
|
+
### DOT Format (Graphviz)
|
|
359
|
+
|
|
360
|
+
```ruby
|
|
361
|
+
puts network.to_dot
|
|
362
|
+
# => Graphviz DOT format
|
|
363
|
+
```
|
|
364
|
+
|
|
293
365
|
### Execution Plan
|
|
294
366
|
|
|
295
367
|
```ruby
|
|
@@ -305,9 +377,58 @@ network.robots # => Hash of name => Robot
|
|
|
305
377
|
network.robot(:billing) # => Robot instance
|
|
306
378
|
network["billing"] # => Robot instance (alias)
|
|
307
379
|
network.available_robots # => Array of Robot instances
|
|
380
|
+
network.memory # => Memory instance (shared)
|
|
308
381
|
network.to_h # => Hash representation
|
|
309
382
|
```
|
|
310
383
|
|
|
384
|
+
## Configuration Inheritance
|
|
385
|
+
|
|
386
|
+
Networks accept a `config:` parameter that establishes default LLM settings for all member robots. This is useful when you want consistent behavior across a pipeline without configuring each robot individually.
|
|
387
|
+
|
|
388
|
+
### Network-Wide Defaults
|
|
389
|
+
|
|
390
|
+
```ruby
|
|
391
|
+
# All robots in this network use the same model and temperature
|
|
392
|
+
shared = RobotLab::RunConfig.new(model: "claude-sonnet-4", temperature: 0.5)
|
|
393
|
+
|
|
394
|
+
network = RobotLab.create_network(name: "pipeline", config: shared) do
|
|
395
|
+
task :analyzer, analyzer_robot, depends_on: :none
|
|
396
|
+
task :writer, writer_robot, depends_on: [:analyzer]
|
|
397
|
+
task :reviewer, reviewer_robot, depends_on: [:writer]
|
|
398
|
+
end
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Per-Task Overrides
|
|
402
|
+
|
|
403
|
+
Individual tasks can override the network's config with their own `config:`:
|
|
404
|
+
|
|
405
|
+
```ruby
|
|
406
|
+
creative_config = RobotLab::RunConfig.new(temperature: 0.9)
|
|
407
|
+
|
|
408
|
+
network = RobotLab.create_network(name: "pipeline", config: shared) do
|
|
409
|
+
task :analyzer, analyzer_robot, depends_on: :none
|
|
410
|
+
task :writer, writer_robot,
|
|
411
|
+
config: creative_config, # writer gets higher temperature
|
|
412
|
+
depends_on: [:analyzer]
|
|
413
|
+
task :reviewer, reviewer_robot, depends_on: [:writer]
|
|
414
|
+
end
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Inheritance Chain
|
|
418
|
+
|
|
419
|
+
The full configuration hierarchy (most-specific wins):
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
RobotLab.config (global)
|
|
423
|
+
-> Network config
|
|
424
|
+
-> Task config
|
|
425
|
+
-> Robot config (from constructor)
|
|
426
|
+
-> Template front matter
|
|
427
|
+
-> Constructor kwargs (model:, temperature:, etc.)
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Each layer only overrides values it explicitly sets. Unset values pass through from the parent.
|
|
431
|
+
|
|
311
432
|
## Best Practices
|
|
312
433
|
|
|
313
434
|
### 1. Keep Robots Focused
|
|
@@ -323,25 +444,14 @@ task :respond, responder, depends_on: [:classify]
|
|
|
323
444
|
task :do_everything, mega_robot, depends_on: :none
|
|
324
445
|
```
|
|
325
446
|
|
|
326
|
-
### 2. Use Context
|
|
447
|
+
### 2. Use Per-Task Context
|
|
327
448
|
|
|
328
|
-
|
|
449
|
+
Pass task-specific configuration through context:
|
|
329
450
|
|
|
330
451
|
```ruby
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
classification = result.context[:classifier]&.last_text_content
|
|
335
|
-
|
|
336
|
-
# Use it in this robot's run
|
|
337
|
-
robot_result = run(
|
|
338
|
-
**extract_run_context(result),
|
|
339
|
-
classification: classification
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
-
result.with_context(@name.to_sym, robot_result).continue(robot_result)
|
|
343
|
-
end
|
|
344
|
-
end
|
|
452
|
+
task :billing, billing_robot,
|
|
453
|
+
context: { department: "billing", max_refund: 500 },
|
|
454
|
+
depends_on: :optional
|
|
345
455
|
```
|
|
346
456
|
|
|
347
457
|
### 3. Handle Missing Results
|
|
@@ -359,8 +469,17 @@ def call(result)
|
|
|
359
469
|
end
|
|
360
470
|
```
|
|
361
471
|
|
|
472
|
+
### 4. Reset Memory Between Runs
|
|
473
|
+
|
|
474
|
+
If reusing a network, reset shared memory between runs:
|
|
475
|
+
|
|
476
|
+
```ruby
|
|
477
|
+
network.reset_memory
|
|
478
|
+
result = network.run(message: "New request")
|
|
479
|
+
```
|
|
480
|
+
|
|
362
481
|
## Next Steps
|
|
363
482
|
|
|
364
483
|
- [Using Tools](using-tools.md) - Add capabilities to robots
|
|
365
|
-
- [Memory Guide](memory.md) -
|
|
484
|
+
- [Memory Guide](memory.md) - Shared memory patterns
|
|
366
485
|
- [API Reference: Network](../api/core/network.md) - Complete API
|
data/docs/guides/index.md
CHANGED
|
@@ -34,10 +34,6 @@ If you're new to RobotLab, start here:
|
|
|
34
34
|
|
|
35
35
|
Real-time streaming of LLM responses
|
|
36
36
|
|
|
37
|
-
- [:octicons-database-24: **Conversation History**](history.md)
|
|
38
|
-
|
|
39
|
-
Persist and restore conversation threads
|
|
40
|
-
|
|
41
37
|
- [:octicons-cpu-24: **Memory System**](memory.md)
|
|
42
38
|
|
|
43
39
|
Share data between robots with the memory system
|
|
@@ -63,6 +59,5 @@ If you're new to RobotLab, start here:
|
|
|
63
59
|
| [Using Tools](using-tools.md) | Add custom capabilities | 10 min |
|
|
64
60
|
| [MCP Integration](mcp-integration.md) | External tool servers | 10 min |
|
|
65
61
|
| [Streaming](streaming.md) | Real-time responses | 5 min |
|
|
66
|
-
| [History](history.md) | Conversation persistence | 10 min |
|
|
67
62
|
| [Memory](memory.md) | Shared data store | 5 min |
|
|
68
63
|
| [Rails Integration](rails-integration.md) | Rails application setup | 15 min |
|
|
@@ -12,13 +12,15 @@ MCP is a protocol that allows LLM applications to connect to external servers th
|
|
|
12
12
|
|
|
13
13
|
## Configuring MCP Servers
|
|
14
14
|
|
|
15
|
-
### At
|
|
15
|
+
### At Robot Level
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
network = RobotLab.create_network do
|
|
19
|
-
name "dev_assistant"
|
|
17
|
+
Use the `mcp:` parameter on `RobotLab.build` to connect a robot to MCP servers:
|
|
20
18
|
|
|
21
|
-
|
|
19
|
+
```ruby
|
|
20
|
+
robot = RobotLab.build(
|
|
21
|
+
name: "coder",
|
|
22
|
+
template: :developer,
|
|
23
|
+
mcp: [
|
|
22
24
|
{
|
|
23
25
|
name: "filesystem",
|
|
24
26
|
transport: {
|
|
@@ -31,40 +33,72 @@ network = RobotLab.create_network do
|
|
|
31
33
|
name: "github",
|
|
32
34
|
transport: {
|
|
33
35
|
type: "stdio",
|
|
34
|
-
command: "mcp-server-github"
|
|
36
|
+
command: "mcp-server-github",
|
|
37
|
+
env: { "GITHUB_TOKEN" => ENV["GITHUB_TOKEN"] }
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
]
|
|
38
|
-
|
|
41
|
+
)
|
|
39
42
|
```
|
|
40
43
|
|
|
41
|
-
###
|
|
44
|
+
### In Template Front Matter
|
|
45
|
+
|
|
46
|
+
MCP servers can be declared directly in a template's YAML front matter, making the template fully self-contained:
|
|
47
|
+
|
|
48
|
+
```markdown title="prompts/github_assistant.md"
|
|
49
|
+
---
|
|
50
|
+
description: GitHub assistant with MCP tool access
|
|
51
|
+
mcp:
|
|
52
|
+
- name: github
|
|
53
|
+
transport: stdio
|
|
54
|
+
command: npx
|
|
55
|
+
args: ["-y", "@modelcontextprotocol/server-github"]
|
|
56
|
+
---
|
|
57
|
+
You are a helpful GitHub assistant with access to GitHub tools via MCP.
|
|
58
|
+
```
|
|
42
59
|
|
|
43
60
|
```ruby
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
# MCP config comes from the template — no mcp: parameter needed
|
|
62
|
+
robot = RobotLab.build(template: :github_assistant)
|
|
63
|
+
```
|
|
46
64
|
|
|
47
|
-
|
|
48
|
-
mcp :inherit
|
|
65
|
+
Constructor `mcp:` overrides frontmatter `mcp:` when provided.
|
|
49
66
|
|
|
50
|
-
|
|
51
|
-
mcp [
|
|
52
|
-
{ name: "filesystem", transport: { type: "stdio", command: "mcp-fs" } }
|
|
53
|
-
]
|
|
67
|
+
### Hierarchical Configuration
|
|
54
68
|
|
|
55
|
-
|
|
56
|
-
mcp :none
|
|
57
|
-
end
|
|
58
|
-
```
|
|
69
|
+
The `mcp:` parameter supports three modes:
|
|
59
70
|
|
|
60
|
-
|
|
71
|
+
| Value | Behavior |
|
|
72
|
+
|-------|----------|
|
|
73
|
+
| `:none` | No MCP servers (default) |
|
|
74
|
+
| `:inherit` | Inherit from network or global config |
|
|
75
|
+
| `[...]` | Explicit array of server configurations |
|
|
61
76
|
|
|
62
77
|
```ruby
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
78
|
+
# Inherit from network/config
|
|
79
|
+
robot = RobotLab.build(
|
|
80
|
+
name: "reader",
|
|
81
|
+
system_prompt: "You help read files.",
|
|
82
|
+
mcp: :inherit
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Disable MCP explicitly
|
|
86
|
+
robot = RobotLab.build(
|
|
87
|
+
name: "calculator",
|
|
88
|
+
system_prompt: "You do math.",
|
|
89
|
+
mcp: :none
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Resolution Order
|
|
94
|
+
|
|
95
|
+
MCP configuration resolves through a hierarchy: **runtime > robot build > network > global config**. Each level can override the previous:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
Global (RobotLab.config.mcp)
|
|
99
|
+
-> Network (task mcp: [...])
|
|
100
|
+
-> Robot (mcp: :inherit | :none | [...])
|
|
101
|
+
-> Runtime (robot.run("msg", mcp: [...]))
|
|
68
102
|
```
|
|
69
103
|
|
|
70
104
|
## Transport Types
|
|
@@ -134,68 +168,54 @@ Streamable HTTP transport with session support:
|
|
|
134
168
|
|
|
135
169
|
## Using MCP Tools
|
|
136
170
|
|
|
137
|
-
Once configured, MCP tools are automatically available to
|
|
171
|
+
Once configured, MCP tools are automatically discovered and made available to the robot. The robot connects to MCP servers on its first `run` call and discovers tools dynamically:
|
|
138
172
|
|
|
139
173
|
```ruby
|
|
140
|
-
|
|
141
|
-
|
|
174
|
+
robot = RobotLab.build(
|
|
175
|
+
name: "helper",
|
|
176
|
+
system_prompt: <<~PROMPT
|
|
177
|
+
You can help users with GitHub tasks.
|
|
178
|
+
Use available tools to search repositories, create issues, etc.
|
|
179
|
+
PROMPT,
|
|
180
|
+
mcp: [
|
|
142
181
|
{ name: "github", transport: { type: "stdio", command: "mcp-server-github" } }
|
|
143
182
|
]
|
|
183
|
+
)
|
|
144
184
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
You can help users with GitHub tasks.
|
|
149
|
-
Use available tools to search repositories, create issues, etc.
|
|
150
|
-
PROMPT
|
|
151
|
-
}
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
# The robot can now use GitHub MCP tools
|
|
155
|
-
state = RobotLab.create_state(message: "Find repositories about machine learning")
|
|
156
|
-
network.run(state: state)
|
|
185
|
+
# MCP tools are automatically available
|
|
186
|
+
result = robot.run("Find repositories about machine learning")
|
|
187
|
+
puts result.last_text_content
|
|
157
188
|
```
|
|
158
189
|
|
|
159
190
|
## Filtering MCP Tools
|
|
160
191
|
|
|
161
|
-
|
|
192
|
+
Use the `tools:` parameter to restrict which tools (including MCP-discovered tools) are available to a robot:
|
|
162
193
|
|
|
163
194
|
```ruby
|
|
164
|
-
robot = RobotLab.build
|
|
165
|
-
name "reader"
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
## MCP Server Configuration
|
|
174
|
-
|
|
175
|
-
### Server Object
|
|
176
|
-
|
|
177
|
-
```ruby
|
|
178
|
-
server = RobotLab::MCP::Server.new(
|
|
179
|
-
name: "my_server",
|
|
180
|
-
transport: {
|
|
181
|
-
type: "stdio",
|
|
182
|
-
command: "my-mcp-server"
|
|
183
|
-
}
|
|
195
|
+
robot = RobotLab.build(
|
|
196
|
+
name: "reader",
|
|
197
|
+
system_prompt: "You help read and search files.",
|
|
198
|
+
mcp: [
|
|
199
|
+
{ name: "filesystem", transport: { type: "stdio", command: "mcp-server-fs" } }
|
|
200
|
+
],
|
|
201
|
+
tools: %w[read_file search_files list_directory] # Only allow specific tools
|
|
184
202
|
)
|
|
185
|
-
|
|
186
|
-
server.name # => "my_server"
|
|
187
|
-
server.transport_type # => "stdio"
|
|
188
|
-
server.to_h # Hash representation
|
|
189
203
|
```
|
|
190
204
|
|
|
191
|
-
|
|
205
|
+
## MCP in Networks
|
|
192
206
|
|
|
193
|
-
|
|
194
|
-
client = RobotLab::MCP::Client.new(server: server)
|
|
195
|
-
client.connect
|
|
207
|
+
When running robots in a network, use per-task MCP configuration:
|
|
196
208
|
|
|
197
|
-
|
|
198
|
-
|
|
209
|
+
```ruby
|
|
210
|
+
network = RobotLab.create_network(name: "dev_pipeline") do
|
|
211
|
+
task :planner, planner_robot, depends_on: :none
|
|
212
|
+
task :coder, coder_robot,
|
|
213
|
+
mcp: [
|
|
214
|
+
{ name: "filesystem", transport: { type: "stdio", command: "mcp-server-fs" } }
|
|
215
|
+
],
|
|
216
|
+
depends_on: [:planner]
|
|
217
|
+
task :reviewer, reviewer_robot, depends_on: [:coder]
|
|
218
|
+
end
|
|
199
219
|
```
|
|
200
220
|
|
|
201
221
|
## Common MCP Servers
|
|
@@ -245,54 +265,70 @@ Tools: `search_repositories`, `create_issue`, `get_file_contents`, etc.
|
|
|
245
265
|
|
|
246
266
|
Tools: `query`, `list_tables`, `describe_table`
|
|
247
267
|
|
|
268
|
+
## MCP Server and Client Objects
|
|
269
|
+
|
|
270
|
+
For programmatic access, you can work with MCP objects directly:
|
|
271
|
+
|
|
272
|
+
```ruby
|
|
273
|
+
# Server configuration
|
|
274
|
+
server = RobotLab::MCP::Server.new(
|
|
275
|
+
name: "my_server",
|
|
276
|
+
transport: {
|
|
277
|
+
type: "stdio",
|
|
278
|
+
command: "my-mcp-server"
|
|
279
|
+
}
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
# Client connection
|
|
283
|
+
client = RobotLab::MCP::Client.new(server)
|
|
284
|
+
client.connect
|
|
285
|
+
|
|
286
|
+
client.connected? # => true
|
|
287
|
+
client.list_tools # => Array of tool definitions
|
|
288
|
+
client.call_tool("search", { query: "ruby" })
|
|
289
|
+
client.list_resources # => Array of resource definitions
|
|
290
|
+
client.disconnect
|
|
291
|
+
```
|
|
292
|
+
|
|
248
293
|
## Error Handling
|
|
249
294
|
|
|
250
295
|
### Connection Errors
|
|
251
296
|
|
|
252
297
|
```ruby
|
|
253
298
|
begin
|
|
254
|
-
|
|
299
|
+
result = robot.run("Search for repos")
|
|
255
300
|
rescue RobotLab::MCPError => e
|
|
256
301
|
puts "MCP Error: #{e.message}"
|
|
257
|
-
# Handle gracefully
|
|
258
302
|
end
|
|
259
303
|
```
|
|
260
304
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
```ruby
|
|
264
|
-
# If async-websocket not installed
|
|
265
|
-
rescue RobotLab::MCPError => e
|
|
266
|
-
if e.message.include?("async-websocket")
|
|
267
|
-
puts "Install async-websocket gem for WebSocket support"
|
|
268
|
-
end
|
|
269
|
-
end
|
|
270
|
-
```
|
|
305
|
+
!!! tip
|
|
306
|
+
MCP connection failures are logged as warnings but do not raise errors by default. The robot will continue without MCP tools if a server is unreachable.
|
|
271
307
|
|
|
272
308
|
## Disconnecting
|
|
273
309
|
|
|
274
|
-
Robots
|
|
310
|
+
Robots can be manually disconnected from MCP servers:
|
|
275
311
|
|
|
276
312
|
```ruby
|
|
277
|
-
robot.disconnect #
|
|
313
|
+
robot.disconnect # Disconnect all MCP clients
|
|
278
314
|
```
|
|
279
315
|
|
|
280
|
-
Networks handle this automatically at the end of a run.
|
|
281
|
-
|
|
282
316
|
## Patterns
|
|
283
317
|
|
|
284
318
|
### Development vs Production
|
|
285
319
|
|
|
286
320
|
```ruby
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
[{ name: "s3", transport: { type: "stdio", command: "mcp-s3" } }]
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
mcp mcp_config
|
|
321
|
+
mcp_config = if Rails.env.development?
|
|
322
|
+
[{ name: "local_fs", transport: { type: "stdio", command: "mcp-fs", args: ["--root", "."] } }]
|
|
323
|
+
else
|
|
324
|
+
[{ name: "s3", transport: { type: "stdio", command: "mcp-s3" } }]
|
|
295
325
|
end
|
|
326
|
+
|
|
327
|
+
robot = RobotLab.build(
|
|
328
|
+
name: "file_handler",
|
|
329
|
+
system_prompt: "You manage files.",
|
|
330
|
+
mcp: mcp_config
|
|
331
|
+
)
|
|
296
332
|
```
|
|
297
333
|
|
|
298
334
|
### Dynamic Server Selection
|
|
@@ -305,9 +341,11 @@ def mcp_servers_for_user(user)
|
|
|
305
341
|
servers
|
|
306
342
|
end
|
|
307
343
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
344
|
+
robot = RobotLab.build(
|
|
345
|
+
name: "assistant",
|
|
346
|
+
system_prompt: "You help the user with connected services.",
|
|
347
|
+
mcp: mcp_servers_for_user(current_user)
|
|
348
|
+
)
|
|
311
349
|
```
|
|
312
350
|
|
|
313
351
|
## Best Practices
|
|
@@ -330,24 +368,25 @@ end
|
|
|
330
368
|
|
|
331
369
|
### 2. Limit Tool Access
|
|
332
370
|
|
|
371
|
+
Restrict which MCP tools are available to a robot using the `tools:` parameter:
|
|
372
|
+
|
|
333
373
|
```ruby
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
374
|
+
robot = RobotLab.build(
|
|
375
|
+
name: "reader",
|
|
376
|
+
system_prompt: "You read and search files.",
|
|
377
|
+
mcp: [{ name: "fs", transport: { type: "stdio", command: "mcp-fs" } }],
|
|
378
|
+
tools: %w[read_file search_files] # No write access
|
|
379
|
+
)
|
|
339
380
|
```
|
|
340
381
|
|
|
341
|
-
### 3.
|
|
382
|
+
### 3. Use Appropriate Transports
|
|
342
383
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
end
|
|
350
|
-
```
|
|
384
|
+
| Transport | Best For |
|
|
385
|
+
|-----------|----------|
|
|
386
|
+
| `stdio` | Local servers, CLI tools |
|
|
387
|
+
| `websocket` | Persistent connections, bidirectional |
|
|
388
|
+
| `sse` | Server push, event streams |
|
|
389
|
+
| `streamable_http` | Remote APIs, session-based |
|
|
351
390
|
|
|
352
391
|
## Next Steps
|
|
353
392
|
|