robot_lab 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +1 -1
- data/lib/robot_lab/mcp/transports/streamable_http.rb +1 -1
- data/lib/robot_lab/version.rb +1 -1
- metadata +3 -253
- data/.architecture/AGENTS.md +0 -32
- data/.architecture/config.yml +0 -8
- data/.architecture/members.yml +0 -60
- data/.architecture/reviews/feature-free-will.md +0 -490
- data/.architecture/reviews/overall-codebase.md +0 -427
- data/.claude/settings.local.json +0 -57
- data/.codex/config.toml +0 -2
- data/.envrc +0 -6
- data/.github/workflows/deploy-github-pages.yml +0 -52
- data/.irbrc +0 -6
- data/.loki +0 -65
- data/.quality/reek_baseline.txt +0 -43
- data/.rubocop.yml +0 -10
- data/Archspec.rb +0 -44
- data/CLAUDE.md +0 -141
- data/COMMITS.md +0 -196
- data/Rakefile +0 -193
- data/_typos.toml +0 -21
- data/agent2agent_review.md +0 -192
- data/agentf_improvements.md +0 -253
- data/agents.md +0 -14
- data/docs/api/core/index.md +0 -147
- data/docs/api/core/memory.md +0 -578
- data/docs/api/core/network.md +0 -553
- data/docs/api/core/result.md +0 -211
- data/docs/api/core/robot.md +0 -1667
- data/docs/api/core/state.md +0 -145
- data/docs/api/core/tool.md +0 -520
- data/docs/api/errors.md +0 -323
- data/docs/api/hooks.md +0 -469
- data/docs/api/index.md +0 -137
- data/docs/api/mcp/client.md +0 -387
- data/docs/api/mcp/index.md +0 -267
- data/docs/api/mcp/server.md +0 -252
- data/docs/api/mcp/transports.md +0 -339
- data/docs/api/messages/index.md +0 -126
- data/docs/api/messages/text-message.md +0 -170
- data/docs/api/messages/tool-call-message.md +0 -206
- data/docs/api/messages/tool-result-message.md +0 -242
- data/docs/api/messages/user-message.md +0 -232
- data/docs/api/skills.md +0 -423
- data/docs/api/streaming/context.md +0 -280
- data/docs/api/streaming/events.md +0 -185
- data/docs/api/streaming/index.md +0 -156
- data/docs/api/support.md +0 -846
- data/docs/architecture/core-concepts.md +0 -575
- data/docs/architecture/index.md +0 -252
- data/docs/architecture/message-flow.md +0 -366
- data/docs/architecture/network-orchestration.md +0 -478
- data/docs/architecture/robot-execution.md +0 -396
- data/docs/architecture/state-management.md +0 -410
- data/docs/assets/css/custom.css +0 -56
- data/docs/assets/images/robot_lab.jpg +0 -0
- data/docs/concepts.md +0 -641
- data/docs/examples/basic-chat.md +0 -258
- data/docs/examples/index.md +0 -425
- data/docs/examples/mcp-server.md +0 -403
- data/docs/examples/multi-robot-network.md +0 -476
- data/docs/examples/tool-usage.md +0 -468
- data/docs/getting-started/configuration.md +0 -645
- data/docs/getting-started/index.md +0 -56
- data/docs/getting-started/installation.md +0 -197
- data/docs/getting-started/quick-start.md +0 -293
- data/docs/guides/building-robots.md +0 -1187
- data/docs/guides/creating-networks.md +0 -736
- data/docs/guides/hooks.md +0 -1206
- data/docs/guides/index.md +0 -78
- data/docs/guides/knowledge.md +0 -219
- data/docs/guides/mcp-integration.md +0 -740
- data/docs/guides/memory.md +0 -458
- data/docs/guides/observability.md +0 -733
- data/docs/guides/streaming.md +0 -323
- data/docs/guides/using-tools.md +0 -780
- data/docs/index.md +0 -280
- data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
- data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
- data/examples/.envrc +0 -3
- data/examples/01_simple_robot.rb +0 -31
- data/examples/02_tools.rb +0 -109
- data/examples/03_network.rb +0 -118
- data/examples/04_mcp.rb +0 -217
- data/examples/05_streaming.rb +0 -137
- data/examples/06_prompt_templates.rb +0 -325
- data/examples/07_network_memory.rb +0 -313
- data/examples/08_llm_config.rb +0 -162
- data/examples/09_chaining.rb +0 -251
- data/examples/10_memory.rb +0 -315
- data/examples/11_network_introspection.rb +0 -239
- data/examples/12_message_bus.rb +0 -67
- data/examples/13_spawn.rb +0 -85
- data/examples/14_rusty_circuit/.envrc +0 -1
- data/examples/14_rusty_circuit/.gitignore +0 -1
- data/examples/14_rusty_circuit/README.md +0 -1
- data/examples/14_rusty_circuit/comic.rb +0 -149
- data/examples/14_rusty_circuit/display.rb +0 -217
- data/examples/14_rusty_circuit/heckler.rb +0 -65
- data/examples/14_rusty_circuit/open_mic.rb +0 -132
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
- data/examples/14_rusty_circuit/scout.rb +0 -165
- data/examples/14_rusty_circuit/show.log +0 -234
- data/examples/15_memory_network_and_bus/.envrc +0 -1
- data/examples/15_memory_network_and_bus/README.md +0 -66
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
- data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
- data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
- data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
- data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
- data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
- data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/memory.json +0 -13
- data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
- data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
- data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
- data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
- data/examples/16_writers_room/.envrc +0 -1
- data/examples/16_writers_room/display.rb +0 -158
- data/examples/16_writers_room/output/.gitignore +0 -4
- data/examples/16_writers_room/output/README.md +0 -69
- data/examples/16_writers_room/output/opus_001.md +0 -263
- data/examples/16_writers_room/output/opus_001_notes.log +0 -470
- data/examples/16_writers_room/output/opus_002.md +0 -245
- data/examples/16_writers_room/output/opus_002_notes.log +0 -546
- data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
- data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
- data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
- data/examples/16_writers_room/prompts/writer.md +0 -37
- data/examples/16_writers_room/room.rb +0 -186
- data/examples/16_writers_room/tools.rb +0 -173
- data/examples/16_writers_room/writer.rb +0 -121
- data/examples/16_writers_room/writers_room.rb +0 -256
- data/examples/17_skills.rb +0 -234
- data/examples/18_rails/.envrc +0 -3
- data/examples/18_rails/.gitignore +0 -5
- data/examples/18_rails/Gemfile +0 -11
- data/examples/18_rails/README.md +0 -67
- data/examples/18_rails/Rakefile +0 -4
- data/examples/18_rails/app/controllers/application_controller.rb +0 -4
- data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
- data/examples/18_rails/app/jobs/application_job.rb +0 -4
- data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
- data/examples/18_rails/app/models/application_record.rb +0 -5
- data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
- data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
- data/examples/18_rails/app/robots/chat_robot.rb +0 -25
- data/examples/18_rails/app/tools/time_tool.rb +0 -9
- data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
- data/examples/18_rails/app/views/chat/index.html.erb +0 -67
- data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
- data/examples/18_rails/bin/dev +0 -7
- data/examples/18_rails/bin/rails +0 -6
- data/examples/18_rails/bin/setup +0 -15
- data/examples/18_rails/config/application.rb +0 -33
- data/examples/18_rails/config/cable.yml +0 -2
- data/examples/18_rails/config/database.yml +0 -5
- data/examples/18_rails/config/environment.rb +0 -4
- data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
- data/examples/18_rails/config/routes.rb +0 -6
- data/examples/18_rails/config.ru +0 -4
- data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
- data/examples/19_token_tracking.rb +0 -138
- data/examples/20_circuit_breaker.rb +0 -147
- data/examples/21_learning_loop.rb +0 -181
- data/examples/22_context_compression.rb +0 -172
- data/examples/23_convergence.rb +0 -133
- data/examples/24_structured_delegation.rb +0 -151
- data/examples/25_history_search/conversation.jsonl +0 -30
- data/examples/25_history_search.rb +0 -133
- data/examples/26_document_store/api_versioning_adr.md +0 -52
- data/examples/26_document_store/incident_postmortem.md +0 -46
- data/examples/26_document_store/postgres_runbook.md +0 -49
- data/examples/26_document_store/redis_caching_guide.md +0 -48
- data/examples/26_document_store/sidekiq_guide.md +0 -51
- data/examples/26_document_store.rb +0 -140
- data/examples/27_incident_response/README.md +0 -65
- data/examples/27_incident_response/incident_response.rb +0 -261
- data/examples/28_mcp_discovery.rb +0 -113
- data/examples/29_ractor_tools.rb +0 -240
- data/examples/30_ractor_network.rb +0 -252
- data/examples/31_launch_assessment.rb +0 -246
- data/examples/32_newsletter_reader.rb +0 -193
- data/examples/33_stock_generator.rb +0 -80
- data/examples/33_stock_predictor.rb +0 -327
- data/examples/34_agentskills.rb +0 -76
- data/examples/35_hooks.rb +0 -266
- data/examples/README.md +0 -550
- data/examples/common.rb +0 -180
- data/examples/prompts/assistant.md +0 -5
- data/examples/prompts/audit_trail.md +0 -8
- data/examples/prompts/billing.md +0 -10
- data/examples/prompts/classifier.md +0 -7
- data/examples/prompts/comedian.md +0 -6
- data/examples/prompts/comedy_critic.md +0 -10
- data/examples/prompts/configurable.md +0 -9
- data/examples/prompts/dispatcher.md +0 -12
- data/examples/prompts/entity_extractor.md +0 -14
- data/examples/prompts/escalation.md +0 -42
- data/examples/prompts/frontmatter_mcp_test.md +0 -9
- data/examples/prompts/frontmatter_named_test.md +0 -5
- data/examples/prompts/frontmatter_tools_test.md +0 -6
- data/examples/prompts/general.md +0 -7
- data/examples/prompts/github_assistant.md +0 -14
- data/examples/prompts/helper.md +0 -4
- data/examples/prompts/incident_responder.md +0 -18
- data/examples/prompts/keyword_extractor.md +0 -11
- data/examples/prompts/llm_config_demo.md +0 -20
- data/examples/prompts/order_support.md +0 -35
- data/examples/prompts/os_advocate.md +0 -13
- data/examples/prompts/os_chief.md +0 -13
- data/examples/prompts/os_editor.md +0 -13
- data/examples/prompts/parameterized_main_test.md +0 -6
- data/examples/prompts/pii_redactor.md +0 -7
- data/examples/prompts/product_support.md +0 -37
- data/examples/prompts/runbook_protocol.md +0 -12
- data/examples/prompts/sentiment_analyzer.md +0 -12
- data/examples/prompts/skill_a_test.md +0 -4
- data/examples/prompts/skill_b_test.md +0 -4
- data/examples/prompts/skill_config_test.md +0 -5
- data/examples/prompts/skill_cycle_a_test.md +0 -6
- data/examples/prompts/skill_cycle_b_test.md +0 -6
- data/examples/prompts/skill_description_test.md +0 -4
- data/examples/prompts/skill_leaf_test.md +0 -4
- data/examples/prompts/skill_nested_test.md +0 -6
- data/examples/prompts/skill_refs_main_test.md +0 -6
- data/examples/prompts/skill_self_ref_test.md +0 -6
- data/examples/prompts/skill_with_mcp_test.md +0 -9
- data/examples/prompts/skill_with_params_test.md +0 -6
- data/examples/prompts/skill_with_robot_name_test.md +0 -5
- data/examples/prompts/skill_with_tools_test.md +0 -6
- data/examples/prompts/sre_compliance.md +0 -10
- data/examples/prompts/structured_output.md +0 -7
- data/examples/prompts/synthesizer.md +0 -17
- data/examples/prompts/technical.md +0 -10
- data/examples/prompts/template_with_skills_test.md +0 -6
- data/examples/prompts/triage.md +0 -22
- data/examples/ruboruby.md +0 -423
- data/examples/run_all.rb +0 -60
- data/examples/xyzzy.rb +0 -97
- data/logfile +0 -8
- data/mkdocs.yml +0 -209
- data/robot_concurrency.md +0 -38
- data/simple_acp_review.md +0 -298
- data/temp.md +0 -6
- data/tool_manifest_plan.md +0 -155
data/docs/examples/mcp-server.md
DELETED
|
@@ -1,403 +0,0 @@
|
|
|
1
|
-
# MCP Server
|
|
2
|
-
|
|
3
|
-
Connecting robots to Model Context Protocol servers for external tool access.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
This example demonstrates how to connect robots to external MCP servers. MCP servers expose tools that robots can discover and invoke automatically. RobotLab supports stdio, HTTP, WebSocket, and SSE transports.
|
|
8
|
-
|
|
9
|
-
The runnable version of everything below is
|
|
10
|
-
[`examples/04_mcp.rb`](https://github.com/MadBomber/robot_lab/blob/main/examples/04_mcp.rb)
|
|
11
|
-
(direct `MCP::Client` usage in Part 1, robot integration in Part 2).
|
|
12
|
-
|
|
13
|
-
> [!WARNING]
|
|
14
|
-
> **Two things are required to actually use MCP tools in a run.** `Robot#run`
|
|
15
|
-
> defaults to `mcp: :none, tools: :none`, so build-time `mcp:` servers are never
|
|
16
|
-
> connected by a plain `robot.run("...")`. Pass both:
|
|
17
|
-
>
|
|
18
|
-
> ```ruby
|
|
19
|
-
> robot.run("...", mcp: :inherit, tools: :inherit)
|
|
20
|
-
> ```
|
|
21
|
-
>
|
|
22
|
-
> `mcp: :inherit` triggers the connection attempt; `tools: :inherit` is what sends
|
|
23
|
-
> the discovered tools to the LLM.
|
|
24
|
-
|
|
25
|
-
> [!WARNING]
|
|
26
|
-
> `transport:` must be a **nested hash**. A flat `transport: stdio` with sibling
|
|
27
|
-
> `command:` / `args:` keys raises
|
|
28
|
-
> `NoMethodError: undefined method 'transform_keys' for an instance of String`
|
|
29
|
-
> internally — and that error is swallowed, so the robot silently builds with zero
|
|
30
|
-
> MCP tools. Connection failures are logged and recorded in
|
|
31
|
-
> `robot.failed_mcp_server_names`; they are never raised.
|
|
32
|
-
|
|
33
|
-
## Using MCP with a Robot
|
|
34
|
-
|
|
35
|
-
The primary pattern is to pass MCP server configurations via `mcp:` or `mcp_servers:` when building a robot:
|
|
36
|
-
|
|
37
|
-
```ruby
|
|
38
|
-
#!/usr/bin/env ruby
|
|
39
|
-
|
|
40
|
-
require "bundler/setup"
|
|
41
|
-
require "robot_lab"
|
|
42
|
-
|
|
43
|
-
# MCP server configuration (stdio transport)
|
|
44
|
-
github_server = {
|
|
45
|
-
name: "github",
|
|
46
|
-
transport: {
|
|
47
|
-
type: "stdio",
|
|
48
|
-
command: "github-mcp-server",
|
|
49
|
-
args: ["stdio"],
|
|
50
|
-
env: {
|
|
51
|
-
"GITHUB_PERSONAL_ACCESS_TOKEN" => ENV.fetch("GITHUB_PERSONAL_ACCESS_TOKEN", "")
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
# Create a robot with MCP server integration
|
|
57
|
-
# Tools are automatically discovered when the robot connects
|
|
58
|
-
robot = RobotLab.build(
|
|
59
|
-
name: "github_assistant",
|
|
60
|
-
system_prompt: <<~PROMPT,
|
|
61
|
-
You are a GitHub assistant with access to repository tools.
|
|
62
|
-
Help users search repos, manage issues, and explore code.
|
|
63
|
-
PROMPT
|
|
64
|
-
mcp: [github_server],
|
|
65
|
-
model: "claude-sonnet-4"
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
# MCP clients are created lazily. connect_mcp! forces the connection now so the
|
|
69
|
-
# counts below are meaningful -- without it both lines print empty results.
|
|
70
|
-
robot.connect_mcp!
|
|
71
|
-
|
|
72
|
-
puts "MCP Servers: #{robot.mcp_clients.keys.join(", ")}"
|
|
73
|
-
puts "MCP Tools: #{robot.mcp_tools.size} discovered"
|
|
74
|
-
puts "Failed servers: #{robot.failed_mcp_server_names.join(", ")}" if robot.failed_mcp_server_names.any?
|
|
75
|
-
|
|
76
|
-
# Run the robot -- both kwargs are required for it to reach the MCP tools
|
|
77
|
-
result = robot.run(
|
|
78
|
-
"What are the top 3 most starred Ruby web frameworks on GitHub?",
|
|
79
|
-
mcp: :inherit,
|
|
80
|
-
tools: :inherit
|
|
81
|
-
)
|
|
82
|
-
puts result.last_text_content
|
|
83
|
-
|
|
84
|
-
# Confirm which tools were sent this turn
|
|
85
|
-
puts "Tools sent: #{robot.chat.tools.size}"
|
|
86
|
-
|
|
87
|
-
# Always disconnect MCP clients when done
|
|
88
|
-
robot.disconnect
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
> [!NOTE]
|
|
92
|
-
> `result.tool_calls` is effectively always empty — it reads the final assistant
|
|
93
|
-
> message, which contains only text once ruby_llm's tool loop has finished. Use the
|
|
94
|
-
> `on_tool_call:` callback or the Hook system to observe MCP tool invocations.
|
|
95
|
-
|
|
96
|
-
## Direct MCP Client Usage
|
|
97
|
-
|
|
98
|
-
You can also use the MCP client directly without a robot:
|
|
99
|
-
|
|
100
|
-
```ruby
|
|
101
|
-
require "robot_lab"
|
|
102
|
-
|
|
103
|
-
# Create and connect MCP client
|
|
104
|
-
github_server = {
|
|
105
|
-
name: "github",
|
|
106
|
-
transport: {
|
|
107
|
-
type: "stdio",
|
|
108
|
-
command: "github-mcp-server",
|
|
109
|
-
args: ["stdio"],
|
|
110
|
-
env: { "GITHUB_PERSONAL_ACCESS_TOKEN" => ENV["GITHUB_PERSONAL_ACCESS_TOKEN"] }
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
# NOTE: the config is a single POSITIONAL argument.
|
|
115
|
-
# MCP::Client.new(name: ..., transport: ...) raises ArgumentError.
|
|
116
|
-
client = RobotLab::MCP::Client.new(github_server)
|
|
117
|
-
client.connect
|
|
118
|
-
|
|
119
|
-
if client.connected?
|
|
120
|
-
# List available tools
|
|
121
|
-
tools = client.list_tools
|
|
122
|
-
tools.each do |tool|
|
|
123
|
-
puts "#{tool[:name]}: #{tool[:description]}"
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Call a tool directly
|
|
127
|
-
result = client.call_tool("search_repositories", {
|
|
128
|
-
query: "language:ruby stars:>1000",
|
|
129
|
-
per_page: 5
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
puts JSON.pretty_generate(result)
|
|
133
|
-
|
|
134
|
-
client.disconnect
|
|
135
|
-
end
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## Multiple MCP Servers
|
|
139
|
-
|
|
140
|
-
A robot can connect to multiple MCP servers simultaneously:
|
|
141
|
-
|
|
142
|
-
```ruby
|
|
143
|
-
filesystem_server = {
|
|
144
|
-
name: "filesystem",
|
|
145
|
-
transport: {
|
|
146
|
-
type: "stdio",
|
|
147
|
-
command: "npx",
|
|
148
|
-
args: ["@modelcontextprotocol/server-filesystem", "/data"]
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
github_server = {
|
|
153
|
-
name: "github",
|
|
154
|
-
transport: {
|
|
155
|
-
type: "stdio",
|
|
156
|
-
command: "github-mcp-server",
|
|
157
|
-
args: ["stdio"],
|
|
158
|
-
env: { "GITHUB_PERSONAL_ACCESS_TOKEN" => ENV["GITHUB_PERSONAL_ACCESS_TOKEN"] }
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
robot = RobotLab.build(
|
|
163
|
-
name: "developer",
|
|
164
|
-
system_prompt: "You help with coding tasks using GitHub and the filesystem.",
|
|
165
|
-
mcp: [filesystem_server, github_server],
|
|
166
|
-
model: "claude-sonnet-4"
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
result = robot.run(
|
|
170
|
-
"Search for Ruby repos with CI configs and list their workflow files",
|
|
171
|
-
mcp: :inherit,
|
|
172
|
-
tools: :inherit
|
|
173
|
-
)
|
|
174
|
-
puts result.last_text_content
|
|
175
|
-
|
|
176
|
-
robot.disconnect
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## MCP in Networks
|
|
180
|
-
|
|
181
|
-
`mcp` and `tools` are the only two fields a network passes down to its member
|
|
182
|
-
robots. Declare them **per task** — the `task` DSL is what reliably reaches the
|
|
183
|
-
robot's `run` call.
|
|
184
|
-
|
|
185
|
-
```ruby
|
|
186
|
-
# Create robots -- leave mcp:/tools: unset in the constructor
|
|
187
|
-
data_analyst = RobotLab.build(
|
|
188
|
-
name: "data_analyst",
|
|
189
|
-
system_prompt: "You analyze data."
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
file_manager = RobotLab.build(
|
|
193
|
-
name: "file_manager",
|
|
194
|
-
system_prompt: "You manage files."
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
# Per-task MCP + tools configuration
|
|
198
|
-
network = RobotLab.create_network(name: "support_with_mcp") do
|
|
199
|
-
task :data_analyst, data_analyst,
|
|
200
|
-
mcp: [github_server],
|
|
201
|
-
tools: :inherit, # send everything discovered
|
|
202
|
-
depends_on: :none
|
|
203
|
-
|
|
204
|
-
task :file_manager, file_manager,
|
|
205
|
-
mcp: [filesystem_server],
|
|
206
|
-
tools: %w[read_file list_directory], # allowlist only these tool names
|
|
207
|
-
depends_on: :optional
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
result = network.run(message: "Analyze the project structure")
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
> [!WARNING]
|
|
214
|
-
> Build-time `:inherit` cuts both ways, so scope it deliberately.
|
|
215
|
-
>
|
|
216
|
-
> For a **standalone** robot it is a trap: `:inherit` resolves against the global
|
|
217
|
-
> level (`:none`), yielding an allowlist of `["none"]` that matches nothing, so the
|
|
218
|
-
> robot sends no tools no matter what you pass at run time.
|
|
219
|
-
>
|
|
220
|
-
> Inside a **network**, build-time `:inherit` is exactly how a robot opts into the
|
|
221
|
-
> network's `config:` list — the parent is resolved at run time from
|
|
222
|
-
> `network_config`. Verified with a network `config:` of `tools: %w[ReadFile]` on a
|
|
223
|
-
> robot holding `ReadFile` and `ListDir`:
|
|
224
|
-
>
|
|
225
|
-
> | Robot constructor | Task line | Tools sent |
|
|
226
|
-
> |---|---|---|
|
|
227
|
-
> | `tools: :inherit` | `tools: :inherit` | `[:read_file]` — network allowlist applied |
|
|
228
|
-
> | (unset) | `tools: :inherit` | `[:read_file, :list_dir]` — allowlist ignored |
|
|
229
|
-
>
|
|
230
|
-
> A network-level `config:` also propagates *only* `mcp` and `tools`. LLM fields
|
|
231
|
-
> (`model`, `temperature`, `max_tokens`, ...) and callbacks (`on_content`) are read
|
|
232
|
-
> from each robot's own config at construction time and are never inherited from
|
|
233
|
-
> the network.
|
|
234
|
-
|
|
235
|
-
## Transport Types
|
|
236
|
-
|
|
237
|
-
The `type:` field must be one of `stdio`, `sse`, `ws`, `websocket`,
|
|
238
|
-
`streamable-http`, or `http`. Anything else raises `ArgumentError` — note that
|
|
239
|
-
`streamable_http` with an underscore is **not** valid.
|
|
240
|
-
|
|
241
|
-
`timeout:` sits alongside `transport:` on the server config and defaults to 15
|
|
242
|
-
seconds. It is normalized: `nil` becomes 15, any value of 1000 or more is treated
|
|
243
|
-
as milliseconds (5000 → 5.0s), and anything under 1 second is floored to 1.
|
|
244
|
-
|
|
245
|
-
> [!NOTE]
|
|
246
|
-
> Only the stdio transport actually enforces `timeout`. The SSE, WebSocket, and
|
|
247
|
-
> StreamableHTTP transports store the value and never reference it.
|
|
248
|
-
|
|
249
|
-
## HTTP Transport
|
|
250
|
-
|
|
251
|
-
Connect to remote MCP servers over HTTP. Requires the `async-http` gem.
|
|
252
|
-
|
|
253
|
-
```ruby
|
|
254
|
-
robot = RobotLab.build(
|
|
255
|
-
name: "remote_assistant",
|
|
256
|
-
system_prompt: "You have access to remote tools.",
|
|
257
|
-
mcp: [
|
|
258
|
-
{
|
|
259
|
-
name: "remote_api",
|
|
260
|
-
timeout: 30,
|
|
261
|
-
transport: {
|
|
262
|
-
type: "http",
|
|
263
|
-
url: "https://mcp.example.com/mcp",
|
|
264
|
-
# Auth is supplied by a callable, not a headers hash
|
|
265
|
-
auth_provider: -> { "Bearer #{ENV['MCP_TOKEN']}" }
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
]
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
result = robot.run("Use the remote tools to check system status", mcp: :inherit, tools: :inherit)
|
|
272
|
-
robot.disconnect
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
> [!WARNING]
|
|
276
|
-
> A `headers:` key in a streamable-http transport config is **silently discarded**.
|
|
277
|
-
> The transport builds its own header hash on every request and only merges in
|
|
278
|
-
> `Authorization` when `auth_provider` is set. Use `auth_provider:` (a proc
|
|
279
|
-
> returning the full header value) for authentication.
|
|
280
|
-
|
|
281
|
-
> [!WARNING]
|
|
282
|
-
> The HTTP and SSE transports set `@connected = true` *before* the MCP
|
|
283
|
-
> initialization handshake completes, so `connected?` returns `true` even against
|
|
284
|
-
> an unreachable host. Treat it as "a connect was attempted", not "the server
|
|
285
|
-
> answered", and check `robot.failed_mcp_server_names` as well.
|
|
286
|
-
|
|
287
|
-
## WebSocket Transport
|
|
288
|
-
|
|
289
|
-
For real-time bidirectional communication. Requires the `async-websocket` gem.
|
|
290
|
-
|
|
291
|
-
```ruby
|
|
292
|
-
robot = RobotLab.build(
|
|
293
|
-
name: "realtime_assistant",
|
|
294
|
-
system_prompt: "You monitor real-time events.",
|
|
295
|
-
mcp: [
|
|
296
|
-
{
|
|
297
|
-
name: "realtime",
|
|
298
|
-
transport: {
|
|
299
|
-
type: "websocket",
|
|
300
|
-
url: "ws://localhost:8765"
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
]
|
|
304
|
-
)
|
|
305
|
-
|
|
306
|
-
result = robot.run("Subscribe to the events channel", mcp: :inherit, tools: :inherit)
|
|
307
|
-
robot.disconnect
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
> [!CAUTION]
|
|
311
|
-
> The WebSocket transport currently raises `NameError` on
|
|
312
|
-
> `Async::HTTP::Endpoint` inside an un-awaited `Async` block. It is not usable as
|
|
313
|
-
> shipped — prefer stdio or HTTP.
|
|
314
|
-
|
|
315
|
-
## SSE Transport
|
|
316
|
-
|
|
317
|
-
Server-Sent Events transport. Requires the `async-http` gem.
|
|
318
|
-
|
|
319
|
-
```ruby
|
|
320
|
-
robot = RobotLab.build(
|
|
321
|
-
name: "sse_assistant",
|
|
322
|
-
system_prompt: "You have access to streaming tools.",
|
|
323
|
-
mcp: [
|
|
324
|
-
{
|
|
325
|
-
name: "streaming_api",
|
|
326
|
-
transport: {
|
|
327
|
-
type: "sse",
|
|
328
|
-
url: "https://api.example.com/sse"
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
]
|
|
332
|
-
)
|
|
333
|
-
|
|
334
|
-
result = robot.run("Stream the latest metrics", mcp: :inherit, tools: :inherit)
|
|
335
|
-
robot.disconnect
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
## Runtime MCP Selection
|
|
339
|
-
|
|
340
|
-
`mcp:` and `tools:` on `robot.run` decide, per call, what the LLM sees:
|
|
341
|
-
|
|
342
|
-
```ruby
|
|
343
|
-
robot = RobotLab.build(
|
|
344
|
-
name: "flexible_bot",
|
|
345
|
-
system_prompt: "You use available tools.",
|
|
346
|
-
mcp: [github_server]
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
# Connect the configured MCP servers and send their tools
|
|
350
|
-
result = robot.run("Search for Ruby repos", mcp: :inherit, tools: :inherit)
|
|
351
|
-
|
|
352
|
-
# The DEFAULT: no MCP connection, no tools sent. Writing these out is
|
|
353
|
-
# redundant -- omitting both kwargs does exactly the same thing.
|
|
354
|
-
result = robot.run("Just answer from your knowledge")
|
|
355
|
-
|
|
356
|
-
# Connect MCP but send only two named tools
|
|
357
|
-
result = robot.run(
|
|
358
|
-
"List the open issues",
|
|
359
|
-
mcp: :inherit,
|
|
360
|
-
tools: %w[list_issues get_issue]
|
|
361
|
-
)
|
|
362
|
-
|
|
363
|
-
robot.disconnect
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
> [!NOTE]
|
|
367
|
-
> `tools:` is a **name allowlist** across every available tool — local and MCP
|
|
368
|
-
> alike. `tools: :none` means "zero tools of any kind", not "MCP only"; there is no
|
|
369
|
-
> switch that selects local tools versus MCP tools.
|
|
370
|
-
|
|
371
|
-
## Running
|
|
372
|
-
|
|
373
|
-
```bash
|
|
374
|
-
# Set API keys
|
|
375
|
-
export ANTHROPIC_API_KEY="your-key"
|
|
376
|
-
export GITHUB_PERSONAL_ACCESS_TOKEN="your-token"
|
|
377
|
-
|
|
378
|
-
# Install MCP server (example: GitHub)
|
|
379
|
-
brew install github-mcp-server
|
|
380
|
-
|
|
381
|
-
# Direct MCP::Client usage + robot integration
|
|
382
|
-
ruby examples/04_mcp.rb
|
|
383
|
-
|
|
384
|
-
# Semantic MCP server selection (no LLM calls)
|
|
385
|
-
ruby examples/28_mcp_discovery.rb
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
## Key Concepts
|
|
389
|
-
|
|
390
|
-
1. **MCP Configuration**: Pass server configs via `mcp:` / `mcp_servers:` on `RobotLab.build` or `Robot.new`. `transport:` must be a nested hash
|
|
391
|
-
2. **Nothing is sent by default**: `run` defaults to `mcp: :none, tools: :none`. Pass `mcp: :inherit, tools: :inherit` to connect and send
|
|
392
|
-
3. **Lazy connection**: MCP clients are created on the first qualifying `run`. Call `robot.connect_mcp!` to connect eagerly — before that, `mcp_clients` and `mcp_tools` are empty
|
|
393
|
-
4. **Failures are silent**: connection errors are logged and recorded in `robot.failed_mcp_server_names`, never raised
|
|
394
|
-
5. **Transport Types**: `stdio`, `sse`, `ws`, `websocket`, `streamable-http`, `http` (`streamable_http` is invalid)
|
|
395
|
-
6. **Tool Filtering**: `tools:` is an allowlist over local *and* MCP tools combined; entries must match how each tool was attached (class-attached → `"ReadFile"`, instance-attached → `"read_file"`)
|
|
396
|
-
7. **Networks**: an explicit list on the `task` line applies directly; to inherit the network's `config:` list instead, build the robot with `tools: :inherit` *and* pass `tools: :inherit` on the task
|
|
397
|
-
8. **Cleanup**: Always call `robot.disconnect` when done to release MCP connections
|
|
398
|
-
|
|
399
|
-
## See Also
|
|
400
|
-
|
|
401
|
-
- [MCP Integration Guide](../guides/mcp-integration.md)
|
|
402
|
-
- [MCP API Reference](../api/mcp/index.md)
|
|
403
|
-
- [Transports](../api/mcp/transports.md)
|