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.
Files changed (256) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +1 -1
  4. data/lib/robot_lab/mcp/transports/streamable_http.rb +1 -1
  5. data/lib/robot_lab/version.rb +1 -1
  6. metadata +3 -253
  7. data/.architecture/AGENTS.md +0 -32
  8. data/.architecture/config.yml +0 -8
  9. data/.architecture/members.yml +0 -60
  10. data/.architecture/reviews/feature-free-will.md +0 -490
  11. data/.architecture/reviews/overall-codebase.md +0 -427
  12. data/.claude/settings.local.json +0 -57
  13. data/.codex/config.toml +0 -2
  14. data/.envrc +0 -6
  15. data/.github/workflows/deploy-github-pages.yml +0 -52
  16. data/.irbrc +0 -6
  17. data/.loki +0 -65
  18. data/.quality/reek_baseline.txt +0 -43
  19. data/.rubocop.yml +0 -10
  20. data/Archspec.rb +0 -44
  21. data/CLAUDE.md +0 -141
  22. data/COMMITS.md +0 -196
  23. data/Rakefile +0 -193
  24. data/_typos.toml +0 -21
  25. data/agent2agent_review.md +0 -192
  26. data/agentf_improvements.md +0 -253
  27. data/agents.md +0 -14
  28. data/docs/api/core/index.md +0 -147
  29. data/docs/api/core/memory.md +0 -578
  30. data/docs/api/core/network.md +0 -553
  31. data/docs/api/core/result.md +0 -211
  32. data/docs/api/core/robot.md +0 -1667
  33. data/docs/api/core/state.md +0 -145
  34. data/docs/api/core/tool.md +0 -520
  35. data/docs/api/errors.md +0 -323
  36. data/docs/api/hooks.md +0 -469
  37. data/docs/api/index.md +0 -137
  38. data/docs/api/mcp/client.md +0 -387
  39. data/docs/api/mcp/index.md +0 -267
  40. data/docs/api/mcp/server.md +0 -252
  41. data/docs/api/mcp/transports.md +0 -339
  42. data/docs/api/messages/index.md +0 -126
  43. data/docs/api/messages/text-message.md +0 -170
  44. data/docs/api/messages/tool-call-message.md +0 -206
  45. data/docs/api/messages/tool-result-message.md +0 -242
  46. data/docs/api/messages/user-message.md +0 -232
  47. data/docs/api/skills.md +0 -423
  48. data/docs/api/streaming/context.md +0 -280
  49. data/docs/api/streaming/events.md +0 -185
  50. data/docs/api/streaming/index.md +0 -156
  51. data/docs/api/support.md +0 -846
  52. data/docs/architecture/core-concepts.md +0 -575
  53. data/docs/architecture/index.md +0 -252
  54. data/docs/architecture/message-flow.md +0 -366
  55. data/docs/architecture/network-orchestration.md +0 -478
  56. data/docs/architecture/robot-execution.md +0 -396
  57. data/docs/architecture/state-management.md +0 -410
  58. data/docs/assets/css/custom.css +0 -56
  59. data/docs/assets/images/robot_lab.jpg +0 -0
  60. data/docs/concepts.md +0 -641
  61. data/docs/examples/basic-chat.md +0 -258
  62. data/docs/examples/index.md +0 -425
  63. data/docs/examples/mcp-server.md +0 -403
  64. data/docs/examples/multi-robot-network.md +0 -476
  65. data/docs/examples/tool-usage.md +0 -468
  66. data/docs/getting-started/configuration.md +0 -645
  67. data/docs/getting-started/index.md +0 -56
  68. data/docs/getting-started/installation.md +0 -197
  69. data/docs/getting-started/quick-start.md +0 -293
  70. data/docs/guides/building-robots.md +0 -1187
  71. data/docs/guides/creating-networks.md +0 -736
  72. data/docs/guides/hooks.md +0 -1206
  73. data/docs/guides/index.md +0 -78
  74. data/docs/guides/knowledge.md +0 -219
  75. data/docs/guides/mcp-integration.md +0 -740
  76. data/docs/guides/memory.md +0 -458
  77. data/docs/guides/observability.md +0 -733
  78. data/docs/guides/streaming.md +0 -323
  79. data/docs/guides/using-tools.md +0 -780
  80. data/docs/index.md +0 -280
  81. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  82. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  83. data/examples/.envrc +0 -3
  84. data/examples/01_simple_robot.rb +0 -31
  85. data/examples/02_tools.rb +0 -109
  86. data/examples/03_network.rb +0 -118
  87. data/examples/04_mcp.rb +0 -217
  88. data/examples/05_streaming.rb +0 -137
  89. data/examples/06_prompt_templates.rb +0 -325
  90. data/examples/07_network_memory.rb +0 -313
  91. data/examples/08_llm_config.rb +0 -162
  92. data/examples/09_chaining.rb +0 -251
  93. data/examples/10_memory.rb +0 -315
  94. data/examples/11_network_introspection.rb +0 -239
  95. data/examples/12_message_bus.rb +0 -67
  96. data/examples/13_spawn.rb +0 -85
  97. data/examples/14_rusty_circuit/.envrc +0 -1
  98. data/examples/14_rusty_circuit/.gitignore +0 -1
  99. data/examples/14_rusty_circuit/README.md +0 -1
  100. data/examples/14_rusty_circuit/comic.rb +0 -149
  101. data/examples/14_rusty_circuit/display.rb +0 -217
  102. data/examples/14_rusty_circuit/heckler.rb +0 -65
  103. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  104. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  105. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  106. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  107. data/examples/14_rusty_circuit/scout.rb +0 -165
  108. data/examples/14_rusty_circuit/show.log +0 -234
  109. data/examples/15_memory_network_and_bus/.envrc +0 -1
  110. data/examples/15_memory_network_and_bus/README.md +0 -66
  111. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  112. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  113. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  114. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  115. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  116. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
  117. data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
  118. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  119. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  120. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  121. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
  122. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
  123. data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
  124. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  125. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  126. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  127. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  128. data/examples/16_writers_room/.envrc +0 -1
  129. data/examples/16_writers_room/display.rb +0 -158
  130. data/examples/16_writers_room/output/.gitignore +0 -4
  131. data/examples/16_writers_room/output/README.md +0 -69
  132. data/examples/16_writers_room/output/opus_001.md +0 -263
  133. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  134. data/examples/16_writers_room/output/opus_002.md +0 -245
  135. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  136. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  137. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  138. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  139. data/examples/16_writers_room/prompts/writer.md +0 -37
  140. data/examples/16_writers_room/room.rb +0 -186
  141. data/examples/16_writers_room/tools.rb +0 -173
  142. data/examples/16_writers_room/writer.rb +0 -121
  143. data/examples/16_writers_room/writers_room.rb +0 -256
  144. data/examples/17_skills.rb +0 -234
  145. data/examples/18_rails/.envrc +0 -3
  146. data/examples/18_rails/.gitignore +0 -5
  147. data/examples/18_rails/Gemfile +0 -11
  148. data/examples/18_rails/README.md +0 -67
  149. data/examples/18_rails/Rakefile +0 -4
  150. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  151. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  152. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  153. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  154. data/examples/18_rails/app/models/application_record.rb +0 -5
  155. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  156. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  157. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  158. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  159. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  160. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  161. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  162. data/examples/18_rails/bin/dev +0 -7
  163. data/examples/18_rails/bin/rails +0 -6
  164. data/examples/18_rails/bin/setup +0 -15
  165. data/examples/18_rails/config/application.rb +0 -33
  166. data/examples/18_rails/config/cable.yml +0 -2
  167. data/examples/18_rails/config/database.yml +0 -5
  168. data/examples/18_rails/config/environment.rb +0 -4
  169. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  170. data/examples/18_rails/config/routes.rb +0 -6
  171. data/examples/18_rails/config.ru +0 -4
  172. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  173. data/examples/19_token_tracking.rb +0 -138
  174. data/examples/20_circuit_breaker.rb +0 -147
  175. data/examples/21_learning_loop.rb +0 -181
  176. data/examples/22_context_compression.rb +0 -172
  177. data/examples/23_convergence.rb +0 -133
  178. data/examples/24_structured_delegation.rb +0 -151
  179. data/examples/25_history_search/conversation.jsonl +0 -30
  180. data/examples/25_history_search.rb +0 -133
  181. data/examples/26_document_store/api_versioning_adr.md +0 -52
  182. data/examples/26_document_store/incident_postmortem.md +0 -46
  183. data/examples/26_document_store/postgres_runbook.md +0 -49
  184. data/examples/26_document_store/redis_caching_guide.md +0 -48
  185. data/examples/26_document_store/sidekiq_guide.md +0 -51
  186. data/examples/26_document_store.rb +0 -140
  187. data/examples/27_incident_response/README.md +0 -65
  188. data/examples/27_incident_response/incident_response.rb +0 -261
  189. data/examples/28_mcp_discovery.rb +0 -113
  190. data/examples/29_ractor_tools.rb +0 -240
  191. data/examples/30_ractor_network.rb +0 -252
  192. data/examples/31_launch_assessment.rb +0 -246
  193. data/examples/32_newsletter_reader.rb +0 -193
  194. data/examples/33_stock_generator.rb +0 -80
  195. data/examples/33_stock_predictor.rb +0 -327
  196. data/examples/34_agentskills.rb +0 -76
  197. data/examples/35_hooks.rb +0 -266
  198. data/examples/README.md +0 -550
  199. data/examples/common.rb +0 -180
  200. data/examples/prompts/assistant.md +0 -5
  201. data/examples/prompts/audit_trail.md +0 -8
  202. data/examples/prompts/billing.md +0 -10
  203. data/examples/prompts/classifier.md +0 -7
  204. data/examples/prompts/comedian.md +0 -6
  205. data/examples/prompts/comedy_critic.md +0 -10
  206. data/examples/prompts/configurable.md +0 -9
  207. data/examples/prompts/dispatcher.md +0 -12
  208. data/examples/prompts/entity_extractor.md +0 -14
  209. data/examples/prompts/escalation.md +0 -42
  210. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  211. data/examples/prompts/frontmatter_named_test.md +0 -5
  212. data/examples/prompts/frontmatter_tools_test.md +0 -6
  213. data/examples/prompts/general.md +0 -7
  214. data/examples/prompts/github_assistant.md +0 -14
  215. data/examples/prompts/helper.md +0 -4
  216. data/examples/prompts/incident_responder.md +0 -18
  217. data/examples/prompts/keyword_extractor.md +0 -11
  218. data/examples/prompts/llm_config_demo.md +0 -20
  219. data/examples/prompts/order_support.md +0 -35
  220. data/examples/prompts/os_advocate.md +0 -13
  221. data/examples/prompts/os_chief.md +0 -13
  222. data/examples/prompts/os_editor.md +0 -13
  223. data/examples/prompts/parameterized_main_test.md +0 -6
  224. data/examples/prompts/pii_redactor.md +0 -7
  225. data/examples/prompts/product_support.md +0 -37
  226. data/examples/prompts/runbook_protocol.md +0 -12
  227. data/examples/prompts/sentiment_analyzer.md +0 -12
  228. data/examples/prompts/skill_a_test.md +0 -4
  229. data/examples/prompts/skill_b_test.md +0 -4
  230. data/examples/prompts/skill_config_test.md +0 -5
  231. data/examples/prompts/skill_cycle_a_test.md +0 -6
  232. data/examples/prompts/skill_cycle_b_test.md +0 -6
  233. data/examples/prompts/skill_description_test.md +0 -4
  234. data/examples/prompts/skill_leaf_test.md +0 -4
  235. data/examples/prompts/skill_nested_test.md +0 -6
  236. data/examples/prompts/skill_refs_main_test.md +0 -6
  237. data/examples/prompts/skill_self_ref_test.md +0 -6
  238. data/examples/prompts/skill_with_mcp_test.md +0 -9
  239. data/examples/prompts/skill_with_params_test.md +0 -6
  240. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  241. data/examples/prompts/skill_with_tools_test.md +0 -6
  242. data/examples/prompts/sre_compliance.md +0 -10
  243. data/examples/prompts/structured_output.md +0 -7
  244. data/examples/prompts/synthesizer.md +0 -17
  245. data/examples/prompts/technical.md +0 -10
  246. data/examples/prompts/template_with_skills_test.md +0 -6
  247. data/examples/prompts/triage.md +0 -22
  248. data/examples/ruboruby.md +0 -423
  249. data/examples/run_all.rb +0 -60
  250. data/examples/xyzzy.rb +0 -97
  251. data/logfile +0 -8
  252. data/mkdocs.yml +0 -209
  253. data/robot_concurrency.md +0 -38
  254. data/simple_acp_review.md +0 -298
  255. data/temp.md +0 -6
  256. data/tool_manifest_plan.md +0 -155
@@ -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)