robot_lab 0.0.1 → 0.0.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 +4 -4
- data/.github/workflows/deploy-github-pages.yml +9 -9
- data/.irbrc +6 -0
- data/CHANGELOG.md +90 -0
- data/README.md +203 -46
- data/Rakefile +70 -1
- data/docs/api/core/index.md +12 -0
- data/docs/api/core/robot.md +478 -130
- data/docs/api/core/tool.md +205 -209
- data/docs/api/history/active-record-adapter.md +174 -94
- data/docs/api/history/config.md +186 -93
- data/docs/api/history/index.md +57 -61
- data/docs/api/history/thread-manager.md +123 -73
- 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/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 +361 -112
- 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 +312 -48
- 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 +275 -162
- 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 +417 -212
- data/docs/guides/creating-networks.md +94 -24
- data/docs/guides/mcp-integration.md +152 -113
- data/docs/guides/memory.md +220 -164
- data/docs/guides/streaming.md +80 -110
- data/docs/guides/using-tools.md +259 -212
- data/docs/index.md +50 -37
- data/examples/01_simple_robot.rb +6 -9
- data/examples/02_tools.rb +6 -9
- data/examples/03_network.rb +13 -14
- 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 +140 -0
- data/examples/09_chaining.rb +223 -0
- data/examples/10_memory.rb +331 -0
- data/examples/11_network_introspection.rb +230 -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 +57 -0
- data/examples/14_rusty_circuit/open_mic.rb +121 -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 +173 -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/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 +1 -1
- data/lib/robot_lab/adapters/openai.rb +2 -1
- 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 +2 -2
- data/lib/robot_lab/robot.rb +523 -249
- data/lib/robot_lab/robot_message.rb +44 -0
- data/lib/robot_lab/robot_result.rb +1 -0
- data/lib/robot_lab/robotic_model.rb +1 -1
- data/lib/robot_lab/streaming/context.rb +1 -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/version.rb +1 -1
- data/lib/robot_lab.rb +66 -55
- metadata +107 -116
- 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/configuration.rb +0 -143
|
@@ -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,8 +119,8 @@ 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 |
|
|
111
125
|
| `depends_on` | `:none`, `[:task1]`, or `:optional` |
|
|
112
126
|
|
|
@@ -117,7 +131,9 @@ Use optional tasks with custom Robot subclasses for intelligent routing:
|
|
|
117
131
|
```ruby
|
|
118
132
|
class ClassifierRobot < RobotLab::Robot
|
|
119
133
|
def call(result)
|
|
120
|
-
|
|
134
|
+
context = extract_run_context(result)
|
|
135
|
+
message = context.delete(:message)
|
|
136
|
+
robot_result = run(message, **context)
|
|
121
137
|
|
|
122
138
|
new_result = result
|
|
123
139
|
.with_context(@name.to_sym, robot_result)
|
|
@@ -194,6 +210,25 @@ result.halted? # Whether execution was halted early
|
|
|
194
210
|
result.continued? # Whether execution continued normally
|
|
195
211
|
```
|
|
196
212
|
|
|
213
|
+
## Broadcasting
|
|
214
|
+
|
|
215
|
+
Networks support a broadcast channel for network-wide announcements:
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
# Register a broadcast handler
|
|
219
|
+
network.on_broadcast do |message|
|
|
220
|
+
case message[:payload][:event]
|
|
221
|
+
when :pause
|
|
222
|
+
puts "Pausing: #{message[:payload][:reason]}"
|
|
223
|
+
when :phase_complete
|
|
224
|
+
puts "Phase complete: #{message[:payload][:phase]}"
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Send broadcasts during execution
|
|
229
|
+
network.broadcast(event: :phase_complete, phase: "analysis")
|
|
230
|
+
```
|
|
231
|
+
|
|
197
232
|
## Patterns
|
|
198
233
|
|
|
199
234
|
### Classifier Pattern
|
|
@@ -203,7 +238,10 @@ Route to specialists based on classification:
|
|
|
203
238
|
```ruby
|
|
204
239
|
class SupportClassifier < RobotLab::Robot
|
|
205
240
|
def call(result)
|
|
206
|
-
|
|
241
|
+
context = extract_run_context(result)
|
|
242
|
+
message = context.delete(:message)
|
|
243
|
+
robot_result = run(message, **context)
|
|
244
|
+
|
|
207
245
|
new_result = result
|
|
208
246
|
.with_context(@name.to_sym, robot_result)
|
|
209
247
|
.continue(robot_result)
|
|
@@ -259,7 +297,9 @@ A robot can halt execution early:
|
|
|
259
297
|
```ruby
|
|
260
298
|
class ValidatorRobot < RobotLab::Robot
|
|
261
299
|
def call(result)
|
|
262
|
-
|
|
300
|
+
context = extract_run_context(result)
|
|
301
|
+
message = context.delete(:message)
|
|
302
|
+
robot_result = run(message, **context)
|
|
263
303
|
|
|
264
304
|
if robot_result.last_text_content.include?("INVALID")
|
|
265
305
|
# Stop the pipeline
|
|
@@ -274,6 +314,30 @@ class ValidatorRobot < RobotLab::Robot
|
|
|
274
314
|
end
|
|
275
315
|
```
|
|
276
316
|
|
|
317
|
+
### Data Passing Between Tasks
|
|
318
|
+
|
|
319
|
+
Access previous task results via context:
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
class ResponderRobot < RobotLab::Robot
|
|
323
|
+
def call(result)
|
|
324
|
+
# Get classifier's output
|
|
325
|
+
classification = result.context[:classifier]&.last_text_content
|
|
326
|
+
|
|
327
|
+
context = extract_run_context(result)
|
|
328
|
+
message = context.delete(:message)
|
|
329
|
+
|
|
330
|
+
# Use classification in the message or context
|
|
331
|
+
robot_result = run(
|
|
332
|
+
"Classification: #{classification}\n\nUser message: #{message}",
|
|
333
|
+
**context
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
result.with_context(@name.to_sym, robot_result).continue(robot_result)
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
```
|
|
340
|
+
|
|
277
341
|
## Visualization
|
|
278
342
|
|
|
279
343
|
### ASCII Visualization
|
|
@@ -290,6 +354,13 @@ puts network.to_mermaid
|
|
|
290
354
|
# => Mermaid graph definition
|
|
291
355
|
```
|
|
292
356
|
|
|
357
|
+
### DOT Format (Graphviz)
|
|
358
|
+
|
|
359
|
+
```ruby
|
|
360
|
+
puts network.to_dot
|
|
361
|
+
# => Graphviz DOT format
|
|
362
|
+
```
|
|
363
|
+
|
|
293
364
|
### Execution Plan
|
|
294
365
|
|
|
295
366
|
```ruby
|
|
@@ -305,6 +376,7 @@ network.robots # => Hash of name => Robot
|
|
|
305
376
|
network.robot(:billing) # => Robot instance
|
|
306
377
|
network["billing"] # => Robot instance (alias)
|
|
307
378
|
network.available_robots # => Array of Robot instances
|
|
379
|
+
network.memory # => Memory instance (shared)
|
|
308
380
|
network.to_h # => Hash representation
|
|
309
381
|
```
|
|
310
382
|
|
|
@@ -323,25 +395,14 @@ task :respond, responder, depends_on: [:classify]
|
|
|
323
395
|
task :do_everything, mega_robot, depends_on: :none
|
|
324
396
|
```
|
|
325
397
|
|
|
326
|
-
### 2. Use Context
|
|
398
|
+
### 2. Use Per-Task Context
|
|
327
399
|
|
|
328
|
-
|
|
400
|
+
Pass task-specific configuration through context:
|
|
329
401
|
|
|
330
402
|
```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
|
|
403
|
+
task :billing, billing_robot,
|
|
404
|
+
context: { department: "billing", max_refund: 500 },
|
|
405
|
+
depends_on: :optional
|
|
345
406
|
```
|
|
346
407
|
|
|
347
408
|
### 3. Handle Missing Results
|
|
@@ -359,8 +420,17 @@ def call(result)
|
|
|
359
420
|
end
|
|
360
421
|
```
|
|
361
422
|
|
|
423
|
+
### 4. Reset Memory Between Runs
|
|
424
|
+
|
|
425
|
+
If reusing a network, reset shared memory between runs:
|
|
426
|
+
|
|
427
|
+
```ruby
|
|
428
|
+
network.reset_memory
|
|
429
|
+
result = network.run(message: "New request")
|
|
430
|
+
```
|
|
431
|
+
|
|
362
432
|
## Next Steps
|
|
363
433
|
|
|
364
434
|
- [Using Tools](using-tools.md) - Add capabilities to robots
|
|
365
|
-
- [Memory Guide](memory.md) -
|
|
435
|
+
- [Memory Guide](memory.md) - Shared memory patterns
|
|
366
436
|
- [API Reference: Network](../api/core/network.md) - Complete API
|
|
@@ -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
|
|