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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-github-pages.yml +9 -9
  3. data/.irbrc +6 -0
  4. data/CHANGELOG.md +90 -0
  5. data/README.md +203 -46
  6. data/Rakefile +70 -1
  7. data/docs/api/core/index.md +12 -0
  8. data/docs/api/core/robot.md +478 -130
  9. data/docs/api/core/tool.md +205 -209
  10. data/docs/api/history/active-record-adapter.md +174 -94
  11. data/docs/api/history/config.md +186 -93
  12. data/docs/api/history/index.md +57 -61
  13. data/docs/api/history/thread-manager.md +123 -73
  14. data/docs/api/mcp/client.md +119 -48
  15. data/docs/api/mcp/index.md +75 -60
  16. data/docs/api/mcp/server.md +120 -136
  17. data/docs/api/mcp/transports.md +172 -184
  18. data/docs/api/streaming/context.md +157 -74
  19. data/docs/api/streaming/events.md +114 -166
  20. data/docs/api/streaming/index.md +74 -72
  21. data/docs/architecture/core-concepts.md +361 -112
  22. data/docs/architecture/index.md +97 -59
  23. data/docs/architecture/message-flow.md +138 -129
  24. data/docs/architecture/network-orchestration.md +197 -50
  25. data/docs/architecture/robot-execution.md +199 -146
  26. data/docs/architecture/state-management.md +255 -187
  27. data/docs/concepts.md +312 -48
  28. data/docs/examples/basic-chat.md +89 -77
  29. data/docs/examples/index.md +222 -47
  30. data/docs/examples/mcp-server.md +207 -203
  31. data/docs/examples/multi-robot-network.md +129 -35
  32. data/docs/examples/rails-application.md +159 -160
  33. data/docs/examples/tool-usage.md +295 -204
  34. data/docs/getting-started/configuration.md +275 -162
  35. data/docs/getting-started/index.md +1 -1
  36. data/docs/getting-started/installation.md +22 -13
  37. data/docs/getting-started/quick-start.md +166 -121
  38. data/docs/guides/building-robots.md +417 -212
  39. data/docs/guides/creating-networks.md +94 -24
  40. data/docs/guides/mcp-integration.md +152 -113
  41. data/docs/guides/memory.md +220 -164
  42. data/docs/guides/streaming.md +80 -110
  43. data/docs/guides/using-tools.md +259 -212
  44. data/docs/index.md +50 -37
  45. data/examples/01_simple_robot.rb +6 -9
  46. data/examples/02_tools.rb +6 -9
  47. data/examples/03_network.rb +13 -14
  48. data/examples/04_mcp.rb +5 -8
  49. data/examples/05_streaming.rb +5 -8
  50. data/examples/06_prompt_templates.rb +42 -37
  51. data/examples/07_network_memory.rb +13 -14
  52. data/examples/08_llm_config.rb +140 -0
  53. data/examples/09_chaining.rb +223 -0
  54. data/examples/10_memory.rb +331 -0
  55. data/examples/11_network_introspection.rb +230 -0
  56. data/examples/12_message_bus.rb +74 -0
  57. data/examples/13_spawn.rb +90 -0
  58. data/examples/14_rusty_circuit/comic.rb +143 -0
  59. data/examples/14_rusty_circuit/display.rb +203 -0
  60. data/examples/14_rusty_circuit/heckler.rb +57 -0
  61. data/examples/14_rusty_circuit/open_mic.rb +121 -0
  62. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +20 -0
  63. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +23 -0
  64. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +20 -0
  65. data/examples/14_rusty_circuit/scout.rb +173 -0
  66. data/examples/14_rusty_circuit/scout_notes.md +89 -0
  67. data/examples/14_rusty_circuit/show.log +234 -0
  68. data/examples/15_memory_network_and_bus/editor_in_chief.rb +24 -0
  69. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +206 -0
  70. data/examples/15_memory_network_and_bus/linux_writer.rb +80 -0
  71. data/examples/15_memory_network_and_bus/os_editor.rb +46 -0
  72. data/examples/15_memory_network_and_bus/os_writer.rb +46 -0
  73. data/examples/15_memory_network_and_bus/output/combined_article.md +13 -0
  74. data/examples/15_memory_network_and_bus/output/final_article.md +15 -0
  75. data/examples/15_memory_network_and_bus/output/linux_draft.md +5 -0
  76. data/examples/15_memory_network_and_bus/output/mac_draft.md +7 -0
  77. data/examples/15_memory_network_and_bus/output/memory.json +13 -0
  78. data/examples/15_memory_network_and_bus/output/revision_1.md +19 -0
  79. data/examples/15_memory_network_and_bus/output/revision_2.md +15 -0
  80. data/examples/15_memory_network_and_bus/output/windows_draft.md +7 -0
  81. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +13 -0
  82. data/examples/15_memory_network_and_bus/prompts/os_chief.md +13 -0
  83. data/examples/15_memory_network_and_bus/prompts/os_editor.md +13 -0
  84. data/examples/README.md +197 -0
  85. data/examples/prompts/{assistant/system.txt.erb → assistant.md} +3 -0
  86. data/examples/prompts/{billing/system.txt.erb → billing.md} +3 -0
  87. data/examples/prompts/{classifier/system.txt.erb → classifier.md} +3 -0
  88. data/examples/prompts/comedian.md +6 -0
  89. data/examples/prompts/comedy_critic.md +10 -0
  90. data/examples/prompts/configurable.md +9 -0
  91. data/examples/prompts/dispatcher.md +12 -0
  92. data/examples/prompts/{entity_extractor/system.txt.erb → entity_extractor.md} +3 -0
  93. data/examples/prompts/{escalation/system.txt.erb → escalation.md} +7 -0
  94. data/examples/prompts/frontmatter_mcp_test.md +9 -0
  95. data/examples/prompts/frontmatter_named_test.md +5 -0
  96. data/examples/prompts/frontmatter_tools_test.md +6 -0
  97. data/examples/prompts/{general/system.txt.erb → general.md} +3 -0
  98. data/examples/prompts/{github_assistant/system.txt.erb → github_assistant.md} +8 -0
  99. data/examples/prompts/{helper/system.txt.erb → helper.md} +3 -0
  100. data/examples/prompts/{keyword_extractor/system.txt.erb → keyword_extractor.md} +3 -0
  101. data/examples/prompts/llm_config_demo.md +20 -0
  102. data/examples/prompts/{order_support/system.txt.erb → order_support.md} +8 -0
  103. data/examples/prompts/os_advocate.md +13 -0
  104. data/examples/prompts/os_chief.md +13 -0
  105. data/examples/prompts/os_editor.md +13 -0
  106. data/examples/prompts/{product_support/system.txt.erb → product_support.md} +7 -0
  107. data/examples/prompts/{sentiment_analyzer/system.txt.erb → sentiment_analyzer.md} +3 -0
  108. data/examples/prompts/{synthesizer/system.txt.erb → synthesizer.md} +3 -0
  109. data/examples/prompts/{technical/system.txt.erb → technical.md} +3 -0
  110. data/examples/prompts/{triage/system.txt.erb → triage.md} +6 -0
  111. data/lib/generators/robot_lab/templates/initializer.rb.tt +1 -1
  112. data/lib/robot_lab/adapters/openai.rb +2 -1
  113. data/lib/robot_lab/ask_user.rb +75 -0
  114. data/lib/robot_lab/config/defaults.yml +121 -0
  115. data/lib/robot_lab/config.rb +183 -0
  116. data/lib/robot_lab/error.rb +6 -0
  117. data/lib/robot_lab/mcp/client.rb +1 -1
  118. data/lib/robot_lab/memory.rb +2 -2
  119. data/lib/robot_lab/robot.rb +523 -249
  120. data/lib/robot_lab/robot_message.rb +44 -0
  121. data/lib/robot_lab/robot_result.rb +1 -0
  122. data/lib/robot_lab/robotic_model.rb +1 -1
  123. data/lib/robot_lab/streaming/context.rb +1 -1
  124. data/lib/robot_lab/tool.rb +108 -172
  125. data/lib/robot_lab/tool_config.rb +1 -1
  126. data/lib/robot_lab/tool_manifest.rb +2 -18
  127. data/lib/robot_lab/version.rb +1 -1
  128. data/lib/robot_lab.rb +66 -55
  129. metadata +107 -116
  130. data/examples/prompts/assistant/user.txt.erb +0 -1
  131. data/examples/prompts/billing/user.txt.erb +0 -1
  132. data/examples/prompts/classifier/user.txt.erb +0 -1
  133. data/examples/prompts/entity_extractor/user.txt.erb +0 -3
  134. data/examples/prompts/escalation/user.txt.erb +0 -34
  135. data/examples/prompts/general/user.txt.erb +0 -1
  136. data/examples/prompts/github_assistant/user.txt.erb +0 -1
  137. data/examples/prompts/helper/user.txt.erb +0 -1
  138. data/examples/prompts/keyword_extractor/user.txt.erb +0 -3
  139. data/examples/prompts/order_support/user.txt.erb +0 -22
  140. data/examples/prompts/product_support/user.txt.erb +0 -32
  141. data/examples/prompts/sentiment_analyzer/user.txt.erb +0 -3
  142. data/examples/prompts/synthesizer/user.txt.erb +0 -15
  143. data/examples/prompts/technical/user.txt.erb +0 -1
  144. data/examples/prompts/triage/user.txt.erb +0 -17
  145. data/lib/robot_lab/configuration.rb +0 -143
@@ -1,139 +1,189 @@
1
- # ThreadManager
1
+ # History::ThreadManager
2
2
 
3
- Manages conversation thread lifecycle.
3
+ Manages conversation thread lifecycle using a `History::Config` for persistence.
4
4
 
5
5
  ## Class: `RobotLab::History::ThreadManager`
6
6
 
7
7
  ```ruby
8
- manager = History::ThreadManager.new(config: history_config)
8
+ config = RobotLab::History::Config.new(...)
9
+ manager = RobotLab::History::ThreadManager.new(config)
10
+
11
+ session_id = manager.create_thread(state: memory, input: "Hello")
12
+ history = manager.get_history(session_id)
9
13
  ```
10
14
 
11
15
  ## Constructor
12
16
 
13
17
  ```ruby
14
- ThreadManager.new(config:)
18
+ ThreadManager.new(config)
15
19
  ```
16
20
 
17
21
  **Parameters:**
18
22
 
19
23
  | Name | Type | Description |
20
24
  |------|------|-------------|
21
- | `config` | `Config` | History configuration |
25
+ | `config` | `Config` | History configuration with persistence callbacks |
26
+
27
+ ## Attributes
28
+
29
+ ### config
30
+
31
+ ```ruby
32
+ manager.config # => RobotLab::History::Config
33
+ ```
34
+
35
+ The history configuration object.
22
36
 
23
37
  ## Methods
24
38
 
25
39
  ### create_thread
26
40
 
27
41
  ```ruby
28
- thread_info = manager.create_thread(state: state, input: input, **context)
42
+ session_id = manager.create_thread(state:, input:)
29
43
  ```
30
44
 
31
- Create a new conversation thread.
45
+ Create a new conversation thread. Delegates to `config.create_thread!` and returns the `session_id` from the result hash.
32
46
 
33
- **Returns:** Hash with `:id` and optional metadata.
47
+ **Parameters:**
48
+
49
+ | Name | Type | Description |
50
+ |------|------|-------------|
51
+ | `state` | `Object` | Current robot memory or state |
52
+ | `input` | `String`, `UserMessage` | Initial user input |
53
+
54
+ **Returns:** `String` -- the session ID for the new thread.
34
55
 
35
56
  ### get_history
36
57
 
37
58
  ```ruby
38
- results = manager.get_history(thread_id: id, **context)
59
+ results = manager.get_history(session_id)
39
60
  ```
40
61
 
41
- Retrieve conversation history.
62
+ Retrieve conversation history for a thread. Delegates to `config.get!`.
63
+
64
+ **Parameters:**
42
65
 
43
- **Returns:** Array of `RobotResult`.
66
+ | Name | Type | Description |
67
+ |------|------|-------------|
68
+ | `session_id` | `String` | Thread identifier |
69
+
70
+ **Returns:** `Array<RobotResult>` -- history of results for the thread.
71
+
72
+ ### append_user_message
73
+
74
+ ```ruby
75
+ manager.append_user_message(session_id:, message:)
76
+ ```
77
+
78
+ Append a user message to the thread. Delegates to `config.append_user_message!`.
79
+
80
+ **Parameters:**
81
+
82
+ | Name | Type | Description |
83
+ |------|------|-------------|
84
+ | `session_id` | `String` | Thread identifier |
85
+ | `message` | `UserMessage` | User message to append |
44
86
 
45
87
  ### append_results
46
88
 
47
89
  ```ruby
48
- manager.append_results(thread_id: id, new_results: results, **context)
90
+ manager.append_results(session_id:, results:)
49
91
  ```
50
92
 
51
- Add results to a thread.
93
+ Append robot results to the thread. Delegates to `config.append_results!`.
52
94
 
53
- ### ensure_thread
95
+ **Parameters:**
96
+
97
+ | Name | Type | Description |
98
+ |------|------|-------------|
99
+ | `session_id` | `String` | Thread identifier |
100
+ | `results` | `Array<RobotResult>` | Results to append |
101
+
102
+ ### load_state
54
103
 
55
104
  ```ruby
56
- thread_id = manager.ensure_thread(state: state, input: input, **context)
105
+ state = manager.load_state(session_id:, state:)
57
106
  ```
58
107
 
59
- Create thread if state doesn't have one, or return existing.
108
+ Load history from a thread into a state/memory object. Retrieves the history, sets the `session_id` on the state, and calls `append_result` for each historical result.
60
109
 
61
- ### load_history
110
+ **Parameters:**
111
+
112
+ | Name | Type | Description |
113
+ |------|------|-------------|
114
+ | `session_id` | `String` | Thread identifier |
115
+ | `state` | `Object` | State or Memory object to populate |
116
+
117
+ **Returns:** The state object with loaded history.
118
+
119
+ ### save_state
62
120
 
63
121
  ```ruby
64
- state = manager.load_history(state: state, **context)
122
+ manager.save_state(session_id:, state:, since_index: 0)
65
123
  ```
66
124
 
67
- Load history into state if thread_id exists.
125
+ Save new results from a state object to the thread. Extracts results from `state.results` starting at `since_index` and appends them.
126
+
127
+ **Parameters:**
128
+
129
+ | Name | Type | Description |
130
+ |------|------|-------------|
131
+ | `session_id` | `String` | Thread identifier |
132
+ | `state` | `Object` | State object with results |
133
+ | `since_index` | `Integer` | Save results from this index (default: 0) |
68
134
 
69
135
  ## Examples
70
136
 
71
137
  ### Basic Usage
72
138
 
73
139
  ```ruby
74
- config = History::Config.new(...)
75
- manager = History::ThreadManager.new(config: config)
76
-
77
- # Start new conversation
78
- state = RobotLab.create_state(message: "Hello")
79
- thread_id = manager.ensure_thread(state: state, input: "Hello")
80
- state.thread_id = thread_id
81
-
82
- # Run and save
83
- result = network.run(state: state)
84
- manager.append_results(thread_id: thread_id, new_results: result.new_results)
85
-
86
- # Continue conversation
87
- state2 = RobotLab.create_state(message: "Follow up")
88
- state2.thread_id = thread_id
89
- state2 = manager.load_history(state: state2)
90
- # state2 now has previous results
140
+ config = RobotLab::History::Config.new(
141
+ create_thread: ->(state:, input:, **) { { session_id: SecureRandom.uuid } },
142
+ get: ->(session_id:, **) { STORE[session_id] || [] },
143
+ append_results: ->(session_id:, new_results:, **) {
144
+ STORE[session_id] ||= []
145
+ STORE[session_id].concat(new_results)
146
+ }
147
+ )
148
+
149
+ manager = RobotLab::History::ThreadManager.new(config)
150
+
151
+ # Start a new conversation
152
+ session_id = manager.create_thread(state: memory, input: "Hello")
153
+
154
+ # Run a robot and save results
155
+ result = robot.run("Hello")
156
+ manager.append_results(session_id: session_id, results: [result])
157
+
158
+ # Later, retrieve history
159
+ history = manager.get_history(session_id)
91
160
  ```
92
161
 
93
- ### In Network
162
+ ### Loading History into Memory
94
163
 
95
164
  ```ruby
96
- # ThreadManager is used internally by Network
97
- network = RobotLab.create_network do
98
- history config
99
- add_robot assistant
100
- end
101
-
102
- # First message - thread created automatically
103
- result1 = network.run(state: state1)
104
- thread_id = result1.state.thread_id
105
-
106
- # Continue - history loaded automatically
107
- state2 = RobotLab.create_state(
108
- message: UserMessage.new("Continue", thread_id: thread_id)
109
- )
110
- result2 = network.run(state: state2)
165
+ manager = RobotLab::History::ThreadManager.new(config)
166
+
167
+ # Create a memory object and load previous conversation
168
+ memory = RobotLab::Memory.new
169
+ manager.load_state(session_id: existing_session_id, state: memory)
170
+
171
+ # Memory now contains previous results and session_id
111
172
  ```
112
173
 
113
- ### Custom Thread Data
174
+ ### Saving Incremental Results
114
175
 
115
176
  ```ruby
116
- manager = History::ThreadManager.new(
117
- config: History::Config.new(
118
- create_thread: ->(state:, input:, metadata:, **) {
119
- Thread.create(
120
- title: input.truncate(50),
121
- metadata: metadata,
122
- created_at: Time.current
123
- )
124
- },
125
- get: ->(thread_id:, **) { Thread.find(thread_id).results },
126
- append_results: ->(thread_id:, new_results:, **) {
127
- Thread.find(thread_id).results.concat(new_results)
128
- }
129
- )
130
- )
177
+ # Save only new results (since the last save point)
178
+ initial_count = memory.results.length
179
+
180
+ result = robot.run("Follow-up question")
181
+ memory.append_result(result)
131
182
 
132
- # Pass metadata when creating thread
133
- thread = manager.create_thread(
134
- state: state,
135
- input: "Help with billing",
136
- metadata: { source: "web", priority: "high" }
183
+ manager.save_state(
184
+ session_id: session_id,
185
+ state: memory,
186
+ since_index: initial_count
137
187
  )
138
188
  ```
139
189
 
@@ -1,49 +1,53 @@
1
1
  # MCP Client
2
2
 
3
- Connects to MCP servers and discovers tools.
3
+ Connects to MCP servers, discovers tools, and invokes them via the Model Context Protocol.
4
4
 
5
5
  ## Class: `RobotLab::MCP::Client`
6
6
 
7
7
  ```ruby
8
8
  client = RobotLab::MCP::Client.new(
9
9
  name: "filesystem",
10
- transport: { type: "stdio", command: "mcp-server-filesystem" }
10
+ transport: { type: "stdio", command: "mcp-server-filesystem", args: ["--root", "/data"] }
11
11
  )
12
12
 
13
13
  client.connect
14
14
  tools = client.list_tools
15
+ result = client.call_tool("readFile", { path: "/data/readme.txt" })
16
+ client.disconnect
15
17
  ```
16
18
 
17
19
  ## Constructor
18
20
 
19
21
  ```ruby
20
- Client.new(name:, transport:)
22
+ Client.new(server_or_config)
21
23
  ```
22
24
 
25
+ Accepts either a `Server` instance or a Hash configuration. When a Hash is provided, it is used to construct a `Server` internally.
26
+
23
27
  **Parameters:**
24
28
 
25
29
  | Name | Type | Description |
26
30
  |------|------|-------------|
27
- | `name` | `String` | Server identifier |
28
- | `transport` | `Hash` | Transport configuration |
31
+ | `server_or_config` | `Server`, `Hash` | Server instance or configuration hash |
29
32
 
30
- ## Attributes
33
+ **Hash Configuration Keys:**
31
34
 
32
- ### name
35
+ | Key | Type | Required | Description |
36
+ |-----|------|----------|-------------|
37
+ | `name` | `String` | Yes | Server identifier |
38
+ | `transport` | `Hash` | Yes | Transport configuration (must include `type`) |
33
39
 
34
- ```ruby
35
- client.name # => String
36
- ```
40
+ **Raises:** `ArgumentError` if the config is neither a `Server` nor a `Hash`.
37
41
 
38
- Server identifier.
42
+ ## Attributes
39
43
 
40
- ### transport
44
+ ### server
41
45
 
42
46
  ```ruby
43
- client.transport # => Transport
47
+ client.server # => RobotLab::MCP::Server
44
48
  ```
45
49
 
46
- The underlying transport connection.
50
+ The MCP server configuration object.
47
51
 
48
52
  ### connected?
49
53
 
@@ -51,38 +55,42 @@ The underlying transport connection.
51
55
  client.connected? # => Boolean
52
56
  ```
53
57
 
54
- Whether the client is connected.
58
+ Whether the client is currently connected to the server.
55
59
 
56
60
  ## Methods
57
61
 
58
62
  ### connect
59
63
 
60
64
  ```ruby
61
- client.connect
65
+ client.connect # => self
62
66
  ```
63
67
 
64
- Establish connection to the MCP server.
68
+ Establish a connection to the MCP server. Creates the appropriate transport based on the server's transport type, then connects. If already connected, returns immediately.
69
+
70
+ Connection failures are logged as warnings and the client remains in a disconnected state (does not raise).
65
71
 
66
72
  ### disconnect
67
73
 
68
74
  ```ruby
69
- client.disconnect
75
+ client.disconnect # => self
70
76
  ```
71
77
 
72
- Close the connection.
78
+ Close the connection to the MCP server. Closes the underlying transport and resets connection state. If not connected, returns immediately.
73
79
 
74
80
  ### list_tools
75
81
 
76
82
  ```ruby
77
- client.list_tools # => Array<Tool>
83
+ client.list_tools # => Array<Hash>
78
84
  ```
79
85
 
80
- Discover available tools from the server.
86
+ Discover available tools from the server. Returns an array of tool definition hashes.
87
+
88
+ **Raises:** `MCPError` if not connected.
81
89
 
82
90
  ### call_tool
83
91
 
84
92
  ```ruby
85
- result = client.call_tool(name, params)
93
+ result = client.call_tool(name, arguments = {})
86
94
  ```
87
95
 
88
96
  Execute a tool on the server.
@@ -92,30 +100,81 @@ Execute a tool on the server.
92
100
  | Name | Type | Description |
93
101
  |------|------|-------------|
94
102
  | `name` | `String` | Tool name |
95
- | `params` | `Hash` | Tool parameters |
103
+ | `arguments` | `Hash` | Tool arguments (default: `{}`) |
104
+
105
+ **Returns:** Tool result content (from the `content` field of the response).
106
+
107
+ **Raises:** `MCPError` if not connected.
96
108
 
97
109
  ### list_resources
98
110
 
99
111
  ```ruby
100
- client.list_resources # => Array<Resource>
112
+ client.list_resources # => Array<Hash>
101
113
  ```
102
114
 
103
- List available resources (if supported).
115
+ List available resources from the server.
116
+
117
+ **Raises:** `MCPError` if not connected.
104
118
 
105
119
  ### read_resource
106
120
 
107
121
  ```ruby
108
- client.read_resource(uri) # => Resource
122
+ client.read_resource(uri) # => Object
109
123
  ```
110
124
 
111
125
  Read a resource by URI.
112
126
 
127
+ **Parameters:**
128
+
129
+ | Name | Type | Description |
130
+ |------|------|-------------|
131
+ | `uri` | `String` | Resource URI |
132
+
133
+ **Raises:** `MCPError` if not connected.
134
+
135
+ ### list_prompts
136
+
137
+ ```ruby
138
+ client.list_prompts # => Array<Hash>
139
+ ```
140
+
141
+ List available prompts from the server.
142
+
143
+ **Raises:** `MCPError` if not connected.
144
+
145
+ ### get_prompt
146
+
147
+ ```ruby
148
+ client.get_prompt(name, arguments = {}) # => Hash
149
+ ```
150
+
151
+ Get a prompt by name with optional arguments.
152
+
153
+ **Parameters:**
154
+
155
+ | Name | Type | Description |
156
+ |------|------|-------------|
157
+ | `name` | `String` | Prompt name |
158
+ | `arguments` | `Hash` | Prompt arguments (default: `{}`) |
159
+
160
+ **Raises:** `MCPError` if not connected.
161
+
162
+ ### to_h
163
+
164
+ ```ruby
165
+ client.to_h # => Hash
166
+ ```
167
+
168
+ Converts the client to a hash representation containing server config and connection status.
169
+
113
170
  ## Transport Configuration
114
171
 
172
+ The transport type is determined by the `type` key in the transport hash of the `Server` configuration.
173
+
115
174
  ### Stdio
116
175
 
117
176
  ```ruby
118
- client = Client.new(
177
+ client = RobotLab::MCP::Client.new(
119
178
  name: "local",
120
179
  transport: {
121
180
  type: "stdio",
@@ -128,10 +187,10 @@ client = Client.new(
128
187
  ### WebSocket
129
188
 
130
189
  ```ruby
131
- client = Client.new(
190
+ client = RobotLab::MCP::Client.new(
132
191
  name: "remote",
133
192
  transport: {
134
- type: "websocket",
193
+ type: "ws",
135
194
  url: "wss://mcp.example.com/ws"
136
195
  }
137
196
  )
@@ -140,7 +199,7 @@ client = Client.new(
140
199
  ### SSE
141
200
 
142
201
  ```ruby
143
- client = Client.new(
202
+ client = RobotLab::MCP::Client.new(
144
203
  name: "streaming",
145
204
  transport: {
146
205
  type: "sse",
@@ -149,14 +208,15 @@ client = Client.new(
149
208
  )
150
209
  ```
151
210
 
152
- ### HTTP
211
+ ### Streamable HTTP
153
212
 
154
213
  ```ruby
155
- client = Client.new(
214
+ client = RobotLab::MCP::Client.new(
156
215
  name: "http",
157
216
  transport: {
158
- type: "http",
159
- url: "https://mcp.example.com/mcp"
217
+ type: "streamable-http",
218
+ url: "https://mcp.example.com/mcp",
219
+ session_id: "optional-session-id"
160
220
  }
161
221
  )
162
222
  ```
@@ -166,7 +226,7 @@ client = Client.new(
166
226
  ### Basic Usage
167
227
 
168
228
  ```ruby
169
- client = Client.new(
229
+ client = RobotLab::MCP::Client.new(
170
230
  name: "github",
171
231
  transport: { type: "stdio", command: "mcp-server-github" }
172
232
  )
@@ -175,7 +235,7 @@ client.connect
175
235
 
176
236
  # List available tools
177
237
  tools = client.list_tools
178
- tools.each { |t| puts "#{t.name}: #{t.description}" }
238
+ tools.each { |t| puts "#{t[:name]}: #{t[:description]}" }
179
239
 
180
240
  # Call a tool
181
241
  result = client.call_tool("search_repositories", { query: "ruby mcp" })
@@ -184,19 +244,32 @@ puts result
184
244
  client.disconnect
185
245
  ```
186
246
 
187
- ### In Robot
247
+ ### From a Server Object
188
248
 
189
249
  ```ruby
190
- robot = RobotLab.build do
191
- name "assistant"
250
+ server = RobotLab::MCP::Server.new(
251
+ name: "neon",
252
+ transport: { type: "ws", url: "ws://localhost:8080" }
253
+ )
192
254
 
193
- mcp [
255
+ client = RobotLab::MCP::Client.new(server)
256
+ client.connect
257
+ ```
258
+
259
+ ### In a Robot
260
+
261
+ ```ruby
262
+ robot = Robot.new(
263
+ name: "assistant",
264
+ system_prompt: "You help with file operations.",
265
+ mcp: [
194
266
  { name: "fs", transport: { type: "stdio", command: "mcp-fs" } }
195
267
  ]
196
- end
268
+ )
197
269
 
198
- # MCP tools are automatically available
199
- robot.tools.each { |t| puts t.name }
270
+ # MCP tools are automatically discovered and available to the LLM
271
+ result = robot.run("Read the contents of /data/config.yml")
272
+ puts result.last_text_content
200
273
  ```
201
274
 
202
275
  ### Error Handling
@@ -205,10 +278,8 @@ robot.tools.each { |t| puts t.name }
205
278
  begin
206
279
  client.connect
207
280
  result = client.call_tool("unknown_tool", {})
208
- rescue RobotLab::MCP::ConnectionError => e
209
- puts "Failed to connect: #{e.message}"
210
- rescue RobotLab::MCP::ToolError => e
211
- puts "Tool error: #{e.message}"
281
+ rescue RobotLab::MCPError => e
282
+ puts "MCP error: #{e.message}"
212
283
  ensure
213
284
  client.disconnect
214
285
  end
@@ -217,5 +288,5 @@ end
217
288
  ## See Also
218
289
 
219
290
  - [MCP Overview](index.md)
291
+ - [Server Configuration](server.md)
220
292
  - [Transports](transports.md)
221
- - [MCP Server](server.md)