robot_lab 0.0.1 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/deploy-github-pages.yml +9 -9
- data/.irbrc +6 -0
- data/CHANGELOG.md +140 -0
- data/README.md +263 -48
- data/Rakefile +71 -1
- data/docs/api/core/index.md +53 -46
- data/docs/api/core/memory.md +200 -154
- data/docs/api/core/network.md +13 -3
- data/docs/api/core/robot.md +490 -130
- data/docs/api/core/state.md +55 -73
- data/docs/api/core/tool.md +205 -209
- data/docs/api/index.md +7 -28
- data/docs/api/mcp/client.md +119 -48
- data/docs/api/mcp/index.md +75 -60
- data/docs/api/mcp/server.md +120 -136
- data/docs/api/mcp/transports.md +172 -184
- data/docs/api/messages/index.md +35 -20
- data/docs/api/messages/text-message.md +67 -21
- data/docs/api/messages/tool-call-message.md +80 -41
- data/docs/api/messages/tool-result-message.md +119 -50
- data/docs/api/messages/user-message.md +48 -24
- data/docs/api/streaming/context.md +157 -74
- data/docs/api/streaming/events.md +114 -166
- data/docs/api/streaming/index.md +74 -72
- data/docs/architecture/core-concepts.md +360 -116
- data/docs/architecture/index.md +97 -59
- data/docs/architecture/message-flow.md +138 -129
- data/docs/architecture/network-orchestration.md +197 -50
- data/docs/architecture/robot-execution.md +199 -146
- data/docs/architecture/state-management.md +255 -187
- data/docs/concepts.md +311 -49
- data/docs/examples/basic-chat.md +89 -77
- data/docs/examples/index.md +222 -47
- data/docs/examples/mcp-server.md +207 -203
- data/docs/examples/multi-robot-network.md +129 -35
- data/docs/examples/rails-application.md +159 -160
- data/docs/examples/tool-usage.md +295 -204
- data/docs/getting-started/configuration.md +347 -154
- data/docs/getting-started/index.md +1 -1
- data/docs/getting-started/installation.md +22 -13
- data/docs/getting-started/quick-start.md +166 -121
- data/docs/guides/building-robots.md +418 -212
- data/docs/guides/creating-networks.md +143 -24
- data/docs/guides/index.md +0 -5
- data/docs/guides/mcp-integration.md +152 -113
- data/docs/guides/memory.md +220 -164
- data/docs/guides/rails-integration.md +244 -162
- data/docs/guides/streaming.md +137 -187
- data/docs/guides/using-tools.md +259 -212
- data/docs/index.md +46 -41
- data/examples/01_simple_robot.rb +6 -9
- data/examples/02_tools.rb +6 -9
- data/examples/03_network.rb +19 -17
- data/examples/04_mcp.rb +5 -8
- data/examples/05_streaming.rb +5 -8
- data/examples/06_prompt_templates.rb +42 -37
- data/examples/07_network_memory.rb +13 -14
- data/examples/08_llm_config.rb +169 -0
- data/examples/09_chaining.rb +262 -0
- data/examples/10_memory.rb +331 -0
- data/examples/11_network_introspection.rb +253 -0
- data/examples/12_message_bus.rb +74 -0
- data/examples/13_spawn.rb +90 -0
- data/examples/14_rusty_circuit/comic.rb +143 -0
- data/examples/14_rusty_circuit/display.rb +203 -0
- data/examples/14_rusty_circuit/heckler.rb +63 -0
- data/examples/14_rusty_circuit/open_mic.rb +123 -0
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +20 -0
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +23 -0
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +20 -0
- data/examples/14_rusty_circuit/scout.rb +156 -0
- data/examples/14_rusty_circuit/scout_notes.md +89 -0
- data/examples/14_rusty_circuit/show.log +234 -0
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +24 -0
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +206 -0
- data/examples/15_memory_network_and_bus/linux_writer.rb +80 -0
- data/examples/15_memory_network_and_bus/os_editor.rb +46 -0
- data/examples/15_memory_network_and_bus/os_writer.rb +46 -0
- data/examples/15_memory_network_and_bus/output/combined_article.md +13 -0
- data/examples/15_memory_network_and_bus/output/final_article.md +15 -0
- data/examples/15_memory_network_and_bus/output/linux_draft.md +5 -0
- data/examples/15_memory_network_and_bus/output/mac_draft.md +7 -0
- data/examples/15_memory_network_and_bus/output/memory.json +13 -0
- data/examples/15_memory_network_and_bus/output/revision_1.md +19 -0
- data/examples/15_memory_network_and_bus/output/revision_2.md +15 -0
- data/examples/15_memory_network_and_bus/output/windows_draft.md +7 -0
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +13 -0
- data/examples/16_writers_room/display.rb +158 -0
- data/examples/16_writers_room/output/.gitignore +2 -0
- data/examples/16_writers_room/output/opus_001.md +263 -0
- data/examples/16_writers_room/output/opus_001_notes.log +470 -0
- data/examples/16_writers_room/prompts/writer.md +37 -0
- data/examples/16_writers_room/room.rb +150 -0
- data/examples/16_writers_room/tools.rb +162 -0
- data/examples/16_writers_room/writer.rb +121 -0
- data/examples/16_writers_room/writers_room.rb +162 -0
- data/examples/README.md +197 -0
- data/examples/prompts/{assistant/system.txt.erb → assistant.md} +3 -0
- data/examples/prompts/{billing/system.txt.erb → billing.md} +3 -0
- data/examples/prompts/{classifier/system.txt.erb → classifier.md} +3 -0
- data/examples/prompts/comedian.md +6 -0
- data/examples/prompts/comedy_critic.md +10 -0
- data/examples/prompts/configurable.md +9 -0
- data/examples/prompts/dispatcher.md +12 -0
- data/examples/prompts/{entity_extractor/system.txt.erb → entity_extractor.md} +3 -0
- data/examples/prompts/{escalation/system.txt.erb → escalation.md} +7 -0
- data/examples/prompts/frontmatter_mcp_test.md +9 -0
- data/examples/prompts/frontmatter_named_test.md +5 -0
- data/examples/prompts/frontmatter_tools_test.md +6 -0
- data/examples/prompts/{general/system.txt.erb → general.md} +3 -0
- data/examples/prompts/{github_assistant/system.txt.erb → github_assistant.md} +8 -0
- data/examples/prompts/{helper/system.txt.erb → helper.md} +3 -0
- data/examples/prompts/{keyword_extractor/system.txt.erb → keyword_extractor.md} +3 -0
- data/examples/prompts/llm_config_demo.md +20 -0
- data/examples/prompts/{order_support/system.txt.erb → order_support.md} +8 -0
- data/examples/prompts/os_advocate.md +13 -0
- data/examples/prompts/os_chief.md +13 -0
- data/examples/prompts/os_editor.md +13 -0
- data/examples/prompts/{product_support/system.txt.erb → product_support.md} +7 -0
- data/examples/prompts/{sentiment_analyzer/system.txt.erb → sentiment_analyzer.md} +3 -0
- data/examples/prompts/{synthesizer/system.txt.erb → synthesizer.md} +3 -0
- data/examples/prompts/{technical/system.txt.erb → technical.md} +3 -0
- data/examples/prompts/{triage/system.txt.erb → triage.md} +6 -0
- data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -13
- data/lib/robot_lab/ask_user.rb +75 -0
- data/lib/robot_lab/config/defaults.yml +121 -0
- data/lib/robot_lab/config.rb +183 -0
- data/lib/robot_lab/error.rb +6 -0
- data/lib/robot_lab/mcp/client.rb +1 -1
- data/lib/robot_lab/memory.rb +10 -34
- data/lib/robot_lab/network.rb +13 -20
- data/lib/robot_lab/robot/bus_messaging.rb +239 -0
- data/lib/robot_lab/robot/mcp_management.rb +88 -0
- data/lib/robot_lab/robot/template_rendering.rb +130 -0
- data/lib/robot_lab/robot.rb +240 -330
- data/lib/robot_lab/robot_message.rb +44 -0
- data/lib/robot_lab/robot_result.rb +1 -0
- data/lib/robot_lab/run_config.rb +184 -0
- data/lib/robot_lab/state_proxy.rb +2 -12
- data/lib/robot_lab/streaming/context.rb +1 -1
- data/lib/robot_lab/task.rb +8 -1
- data/lib/robot_lab/tool.rb +108 -172
- data/lib/robot_lab/tool_config.rb +1 -1
- data/lib/robot_lab/tool_manifest.rb +2 -18
- data/lib/robot_lab/utils.rb +39 -0
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab.rb +89 -57
- data/mkdocs.yml +0 -11
- metadata +121 -135
- data/docs/api/adapters/anthropic.md +0 -121
- data/docs/api/adapters/gemini.md +0 -133
- data/docs/api/adapters/index.md +0 -104
- data/docs/api/adapters/openai.md +0 -134
- data/docs/api/history/active-record-adapter.md +0 -195
- data/docs/api/history/config.md +0 -191
- data/docs/api/history/index.md +0 -132
- data/docs/api/history/thread-manager.md +0 -144
- data/docs/guides/history.md +0 -359
- data/examples/prompts/assistant/user.txt.erb +0 -1
- data/examples/prompts/billing/user.txt.erb +0 -1
- data/examples/prompts/classifier/user.txt.erb +0 -1
- data/examples/prompts/entity_extractor/user.txt.erb +0 -3
- data/examples/prompts/escalation/user.txt.erb +0 -34
- data/examples/prompts/general/user.txt.erb +0 -1
- data/examples/prompts/github_assistant/user.txt.erb +0 -1
- data/examples/prompts/helper/user.txt.erb +0 -1
- data/examples/prompts/keyword_extractor/user.txt.erb +0 -3
- data/examples/prompts/order_support/user.txt.erb +0 -22
- data/examples/prompts/product_support/user.txt.erb +0 -32
- data/examples/prompts/sentiment_analyzer/user.txt.erb +0 -3
- data/examples/prompts/synthesizer/user.txt.erb +0 -15
- data/examples/prompts/technical/user.txt.erb +0 -1
- data/examples/prompts/triage/user.txt.erb +0 -17
- data/lib/robot_lab/adapters/anthropic.rb +0 -163
- data/lib/robot_lab/adapters/base.rb +0 -85
- data/lib/robot_lab/adapters/gemini.rb +0 -193
- data/lib/robot_lab/adapters/openai.rb +0 -159
- data/lib/robot_lab/adapters/registry.rb +0 -81
- data/lib/robot_lab/configuration.rb +0 -143
- data/lib/robot_lab/errors.rb +0 -70
- data/lib/robot_lab/history/active_record_adapter.rb +0 -146
- data/lib/robot_lab/history/config.rb +0 -115
- data/lib/robot_lab/history/thread_manager.rb +0 -93
- data/lib/robot_lab/robotic_model.rb +0 -324
data/docs/api/mcp/transports.md
CHANGED
|
@@ -1,264 +1,252 @@
|
|
|
1
1
|
# MCP Transports
|
|
2
2
|
|
|
3
|
-
Communication methods for MCP connections.
|
|
3
|
+
Communication methods for MCP client-server connections.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
Transports handle the low-level communication between MCP
|
|
7
|
+
Transports handle the low-level communication between `MCP::Client` and external MCP servers. All transports implement the same interface defined by `Transports::Base`, using JSON-RPC 2.0 for message exchange and the MCP protocol version `2024-11-05` for initialization.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
RobotLab provides four built-in transport types:
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
| Transport | Class | Use Case |
|
|
12
|
+
|-----------|-------|----------|
|
|
13
|
+
| Stdio | `Transports::Stdio` | Local subprocess servers |
|
|
14
|
+
| WebSocket | `Transports::WebSocket` | Real-time bidirectional |
|
|
15
|
+
| SSE | `Transports::SSE` | Server-sent events |
|
|
16
|
+
| Streamable HTTP | `Transports::StreamableHTTP` | HTTP with session support |
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
## Base Interface
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
{
|
|
17
|
-
type: "stdio",
|
|
18
|
-
command: "npx",
|
|
19
|
-
args: ["@modelcontextprotocol/server-filesystem", "/path"],
|
|
20
|
-
env: { "DEBUG" => "true" }
|
|
21
|
-
}
|
|
22
|
-
```
|
|
20
|
+
All transports inherit from `RobotLab::MCP::Transports::Base` and implement:
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
```ruby
|
|
23
|
+
class RobotLab::MCP::Transports::Base
|
|
24
|
+
attr_reader :config # => Hash (symbolized keys)
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
def connect # Establish connection, returns self
|
|
27
|
+
def send_request(message) # Send JSON-RPC message, returns Hash response
|
|
28
|
+
def close # Close connection, returns self
|
|
29
|
+
def connected? # Returns Boolean
|
|
30
|
+
end
|
|
31
|
+
```
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
## Stdio Transport
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
- CLI tools
|
|
37
|
-
- Subprocess servers
|
|
35
|
+
**Class:** `RobotLab::MCP::Transports::Stdio`
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
Spawns a subprocess and communicates via stdin/stdout using JSON-RPC messages (one per line). Automatically sends MCP `initialize` and `notifications/initialized` on connect.
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
### Configuration
|
|
42
40
|
|
|
43
41
|
```ruby
|
|
44
42
|
{
|
|
45
|
-
type: "
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
type: "stdio",
|
|
44
|
+
command: "mcp-server-filesystem", # Required: executable command
|
|
45
|
+
args: ["--root", "/data"], # Optional: command arguments
|
|
46
|
+
env: { "DEBUG" => "true" } # Optional: environment variables
|
|
48
47
|
}
|
|
49
48
|
```
|
|
50
49
|
|
|
51
|
-
|
|
50
|
+
| Key | Type | Required | Description |
|
|
51
|
+
|-----|------|----------|-------------|
|
|
52
|
+
| `command` | `String` | Yes | Executable command to spawn |
|
|
53
|
+
| `args` | `Array<String>` | No | Command arguments |
|
|
54
|
+
| `env` | `Hash` | No | Environment variables (merged with current env) |
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|--------|------|-------------|
|
|
55
|
-
| `url` | `String` | WebSocket endpoint |
|
|
56
|
-
| `headers` | `Hash` | HTTP headers |
|
|
57
|
-
| `ping_interval` | `Integer` | Keep-alive interval (seconds) |
|
|
56
|
+
### Behavior
|
|
58
57
|
|
|
59
|
-
|
|
58
|
+
- Uses `Open3.popen3` to spawn the subprocess
|
|
59
|
+
- Writes JSON-RPC messages to stdin (one per line)
|
|
60
|
+
- Reads responses from stdout, skipping notifications (messages without `id`)
|
|
61
|
+
- `connected?` returns `true` when the subprocess is alive
|
|
62
|
+
- `close` terminates stdin, stdout, stderr, and kills the subprocess
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
- Real-time applications
|
|
63
|
-
- Long-running connections
|
|
64
|
+
### Example
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
```ruby
|
|
67
|
+
transport = RobotLab::MCP::Transports::Stdio.new(
|
|
68
|
+
command: "mcp-server-filesystem",
|
|
69
|
+
args: ["--root", "/data"],
|
|
70
|
+
env: { "DEBUG" => "true" }
|
|
71
|
+
)
|
|
66
72
|
|
|
67
|
-
|
|
73
|
+
transport.connect
|
|
74
|
+
response = transport.send_request({ jsonrpc: "2.0", id: 1, method: "tools/list" })
|
|
75
|
+
transport.close
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## WebSocket Transport
|
|
79
|
+
|
|
80
|
+
**Class:** `RobotLab::MCP::Transports::WebSocket`
|
|
81
|
+
|
|
82
|
+
Uses `async-websocket` for non-blocking bidirectional communication. Requires the `async-websocket` gem.
|
|
83
|
+
|
|
84
|
+
### Configuration
|
|
68
85
|
|
|
69
86
|
```ruby
|
|
70
87
|
{
|
|
71
|
-
type: "
|
|
72
|
-
url: "
|
|
73
|
-
headers: { "Authorization" => "Bearer token" }
|
|
88
|
+
type: "ws", # or "websocket"
|
|
89
|
+
url: "wss://mcp.example.com/ws" # Required: WebSocket endpoint
|
|
74
90
|
}
|
|
75
91
|
```
|
|
76
92
|
|
|
77
|
-
|
|
93
|
+
| Key | Type | Required | Description |
|
|
94
|
+
|-----|------|----------|-------------|
|
|
95
|
+
| `url` | `String` | Yes | WebSocket endpoint URL |
|
|
96
|
+
|
|
97
|
+
### Behavior
|
|
78
98
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
| `post_url` | `String` | Endpoint for client messages |
|
|
99
|
+
- Uses `Async::WebSocket::Client.connect` within an `Async` block
|
|
100
|
+
- Sends JSON-RPC messages as JSON strings
|
|
101
|
+
- Reads responses synchronously within the async context
|
|
102
|
+
- Raises `MCPError` if the `async-websocket` gem is not installed
|
|
84
103
|
|
|
85
|
-
|
|
104
|
+
### Example
|
|
86
105
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
106
|
+
```ruby
|
|
107
|
+
transport = RobotLab::MCP::Transports::WebSocket.new(
|
|
108
|
+
url: "ws://localhost:8080"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
transport.connect
|
|
112
|
+
response = transport.send_request({ jsonrpc: "2.0", id: 1, method: "tools/list" })
|
|
113
|
+
transport.close
|
|
114
|
+
```
|
|
90
115
|
|
|
91
|
-
|
|
116
|
+
## SSE Transport
|
|
92
117
|
|
|
93
|
-
|
|
118
|
+
**Class:** `RobotLab::MCP::Transports::SSE`
|
|
119
|
+
|
|
120
|
+
Uses `async-http` for HTTP-based communication. Sends requests via HTTP POST and reads responses. Requires the `async-http` gem.
|
|
121
|
+
|
|
122
|
+
### Configuration
|
|
94
123
|
|
|
95
124
|
```ruby
|
|
96
125
|
{
|
|
97
|
-
type: "
|
|
98
|
-
url: "
|
|
99
|
-
headers: { "Authorization" => "Bearer token" },
|
|
100
|
-
timeout: 30
|
|
126
|
+
type: "sse",
|
|
127
|
+
url: "http://localhost:8080/sse" # Required: SSE endpoint
|
|
101
128
|
}
|
|
102
129
|
```
|
|
103
130
|
|
|
104
|
-
|
|
131
|
+
| Key | Type | Required | Description |
|
|
132
|
+
|-----|------|----------|-------------|
|
|
133
|
+
| `url` | `String` | Yes | SSE/HTTP endpoint URL |
|
|
105
134
|
|
|
106
|
-
|
|
107
|
-
|--------|------|-------------|
|
|
108
|
-
| `url` | `String` | HTTP endpoint |
|
|
109
|
-
| `headers` | `Hash` | HTTP headers |
|
|
110
|
-
| `timeout` | `Integer` | Request timeout (seconds) |
|
|
111
|
-
| `retry` | `Integer` | Retry attempts |
|
|
135
|
+
### Behavior
|
|
112
136
|
|
|
113
|
-
|
|
137
|
+
- Creates an `Async::HTTP::Client` on connect
|
|
138
|
+
- Sends JSON-RPC messages via HTTP POST with `Content-Type: application/json`
|
|
139
|
+
- Reads and parses JSON response body
|
|
140
|
+
- Raises `MCPError` if the `async-http` gem is not installed
|
|
114
141
|
|
|
115
|
-
|
|
116
|
-
- Stateless operations
|
|
117
|
-
- Load-balanced servers
|
|
118
|
-
|
|
119
|
-
## Examples
|
|
120
|
-
|
|
121
|
-
### Local Server with Stdio
|
|
142
|
+
### Example
|
|
122
143
|
|
|
123
144
|
```ruby
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
args: ["@modelcontextprotocol/server-filesystem", "/home/user/docs"]
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
]
|
|
135
|
-
end
|
|
145
|
+
transport = RobotLab::MCP::Transports::SSE.new(
|
|
146
|
+
url: "http://localhost:8080/sse"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
transport.connect
|
|
150
|
+
response = transport.send_request({ jsonrpc: "2.0", id: 1, method: "tools/list" })
|
|
151
|
+
transport.close
|
|
136
152
|
```
|
|
137
153
|
|
|
138
|
-
|
|
154
|
+
## Streamable HTTP Transport
|
|
139
155
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
{
|
|
144
|
-
name: "remote_tools",
|
|
145
|
-
transport: {
|
|
146
|
-
type: "websocket",
|
|
147
|
-
url: "wss://tools.example.com/mcp",
|
|
148
|
-
headers: {
|
|
149
|
-
"Authorization" => "Bearer #{ENV['MCP_TOKEN']}"
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
]
|
|
154
|
-
end
|
|
155
|
-
```
|
|
156
|
+
**Class:** `RobotLab::MCP::Transports::StreamableHTTP`
|
|
157
|
+
|
|
158
|
+
HTTP-based transport with session management and optional authentication. Supports session IDs for maintaining server-side state across requests. Requires the `async-http` gem.
|
|
156
159
|
|
|
157
|
-
###
|
|
160
|
+
### Configuration
|
|
158
161
|
|
|
159
162
|
```ruby
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
},
|
|
167
|
-
# Remote API
|
|
168
|
-
{
|
|
169
|
-
name: "api",
|
|
170
|
-
transport: {
|
|
171
|
-
type: "http",
|
|
172
|
-
url: "https://api.example.com/mcp"
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
# Real-time service
|
|
176
|
-
{
|
|
177
|
-
name: "realtime",
|
|
178
|
-
transport: {
|
|
179
|
-
type: "websocket",
|
|
180
|
-
url: "wss://realtime.example.com/mcp"
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
]
|
|
184
|
-
end
|
|
163
|
+
{
|
|
164
|
+
type: "streamable-http", # or "http"
|
|
165
|
+
url: "https://server.smithery.ai/neon/mcp", # Required: HTTP endpoint
|
|
166
|
+
session_id: "abc123", # Optional: session identifier
|
|
167
|
+
auth_provider: -> { "Bearer #{token}" } # Optional: auth callback
|
|
168
|
+
}
|
|
185
169
|
```
|
|
186
170
|
|
|
187
|
-
|
|
171
|
+
| Key | Type | Required | Description |
|
|
172
|
+
|-----|------|----------|-------------|
|
|
173
|
+
| `url` | `String` | Yes | HTTP endpoint URL |
|
|
174
|
+
| `session_id` | `String` | No | Pre-existing session identifier |
|
|
175
|
+
| `auth_provider` | `Proc` | No | Callback returning Authorization header value |
|
|
176
|
+
|
|
177
|
+
### Behavior
|
|
178
|
+
|
|
179
|
+
- Creates an `Async::HTTP::Client` on connect
|
|
180
|
+
- Sends MCP `initialize` on connect; if no session ID was provided, extracts it from the server response (`serverInfo.sessionId`)
|
|
181
|
+
- Sends `X-Session-ID` header with each request when a session ID is available
|
|
182
|
+
- Calls `auth_provider` for each request to populate the `Authorization` header
|
|
183
|
+
- Exposes `session_id` reader for accessing the current session ID
|
|
184
|
+
- Raises `MCPError` if the `async-http` gem is not installed
|
|
185
|
+
|
|
186
|
+
### Example
|
|
188
187
|
|
|
189
188
|
```ruby
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def connect
|
|
196
|
-
# Establish connection
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
def disconnect
|
|
200
|
-
# Close connection
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
def send(message)
|
|
204
|
-
# Send message to server
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
def receive
|
|
208
|
-
# Receive message from server
|
|
209
|
-
end
|
|
210
|
-
end
|
|
189
|
+
transport = RobotLab::MCP::Transports::StreamableHTTP.new(
|
|
190
|
+
url: "https://server.smithery.ai/neon/mcp",
|
|
191
|
+
auth_provider: -> { "Bearer #{ENV['MCP_TOKEN']}" }
|
|
192
|
+
)
|
|
211
193
|
|
|
212
|
-
|
|
194
|
+
transport.connect
|
|
195
|
+
puts transport.session_id # => assigned by server or pre-configured
|
|
213
196
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
type: "custom",
|
|
217
|
-
option1: "value1"
|
|
218
|
-
}
|
|
197
|
+
response = transport.send_request({ jsonrpc: "2.0", id: 1, method: "tools/list" })
|
|
198
|
+
transport.close
|
|
219
199
|
```
|
|
220
200
|
|
|
221
201
|
## Connection Lifecycle
|
|
222
202
|
|
|
223
|
-
|
|
224
|
-
sequenceDiagram
|
|
225
|
-
participant C as Client
|
|
226
|
-
participant T as Transport
|
|
227
|
-
participant S as Server
|
|
203
|
+
All transports follow the same lifecycle:
|
|
228
204
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
205
|
+
1. **Create** -- instantiate with configuration hash
|
|
206
|
+
2. **Connect** -- establish connection and perform MCP protocol initialization
|
|
207
|
+
3. **Request/Response** -- send JSON-RPC requests, receive responses
|
|
208
|
+
4. **Close** -- tear down connection and release resources
|
|
233
209
|
|
|
234
|
-
|
|
235
|
-
T->>S: Forward request
|
|
236
|
-
S-->>T: Response
|
|
237
|
-
T-->>C: receive() → response
|
|
210
|
+
Each transport sends the MCP `initialize` message during connect:
|
|
238
211
|
|
|
239
|
-
|
|
240
|
-
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"jsonrpc": "2.0",
|
|
215
|
+
"id": 0,
|
|
216
|
+
"method": "initialize",
|
|
217
|
+
"params": {
|
|
218
|
+
"protocolVersion": "2024-11-05",
|
|
219
|
+
"capabilities": {},
|
|
220
|
+
"clientInfo": {
|
|
221
|
+
"name": "RobotLab",
|
|
222
|
+
"version": "<current version>"
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
241
226
|
```
|
|
242
227
|
|
|
243
228
|
## Error Handling
|
|
244
229
|
|
|
230
|
+
All transports raise `RobotLab::MCPError` for connection and communication failures:
|
|
231
|
+
|
|
245
232
|
```ruby
|
|
246
233
|
begin
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
puts "WebSocket error: #{e.message}"
|
|
254
|
-
when "http"
|
|
255
|
-
puts "HTTP error: #{e.status} - #{e.message}"
|
|
256
|
-
end
|
|
234
|
+
transport.connect
|
|
235
|
+
transport.send_request(message)
|
|
236
|
+
rescue RobotLab::MCPError => e
|
|
237
|
+
puts "Transport error: #{e.message}"
|
|
238
|
+
ensure
|
|
239
|
+
transport.close
|
|
257
240
|
end
|
|
258
241
|
```
|
|
259
242
|
|
|
243
|
+
Specific error cases:
|
|
244
|
+
- **Not connected** -- calling `send_request` before `connect` raises `MCPError`
|
|
245
|
+
- **Missing gem** -- WebSocket, SSE, and HTTP transports raise `MCPError` with a `LoadError` message if required gems are not installed
|
|
246
|
+
- **No response** -- Stdio transport raises `MCPError` if the subprocess produces no output
|
|
247
|
+
|
|
260
248
|
## See Also
|
|
261
249
|
|
|
262
250
|
- [MCP Overview](index.md)
|
|
263
251
|
- [MCP Client](client.md)
|
|
264
|
-
- [
|
|
252
|
+
- [Server Configuration](server.md)
|
data/docs/api/messages/index.md
CHANGED
|
@@ -8,60 +8,75 @@ RobotLab uses a structured message system to represent conversations between use
|
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
10
|
# User input
|
|
11
|
-
user_msg = UserMessage.new("Hello",
|
|
11
|
+
user_msg = UserMessage.new("Hello", session_id: "123")
|
|
12
12
|
|
|
13
13
|
# Assistant response
|
|
14
|
-
text_msg = TextMessage.new("Hi there!")
|
|
14
|
+
text_msg = TextMessage.new(role: "assistant", content: "Hi there!")
|
|
15
15
|
|
|
16
16
|
# Tool interaction
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
tool = ToolMessage.new(id: "call_1", name: "get_weather", input: { city: "NYC" })
|
|
18
|
+
tool_call = ToolCallMessage.new(role: "assistant", tools: [tool])
|
|
19
|
+
tool_result = ToolResultMessage.new(tool: tool, content: { data: { temp: 72 } })
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
## Message Hierarchy
|
|
22
23
|
|
|
23
24
|
```
|
|
24
25
|
Message (base)
|
|
25
|
-
├──
|
|
26
|
-
├──
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
├── TextMessage - role + text content
|
|
27
|
+
├── ToolCallMessage - role + Array<ToolMessage>
|
|
28
|
+
└── ToolResultMessage - tool + result content
|
|
29
|
+
|
|
30
|
+
UserMessage - Standalone (not a Message subclass)
|
|
31
|
+
ToolMessage - Standalone (not a Message subclass)
|
|
31
32
|
```
|
|
32
33
|
|
|
33
34
|
## Common Interface
|
|
34
35
|
|
|
35
|
-
All
|
|
36
|
+
All Message subclasses implement:
|
|
36
37
|
|
|
37
38
|
```ruby
|
|
38
|
-
message.role # =>
|
|
39
|
+
message.role # => String ("user", "assistant", "tool_result")
|
|
39
40
|
message.content # => String or structured data
|
|
41
|
+
message.type # => String ("text", "tool_call", "tool_result")
|
|
40
42
|
message.to_h # => Hash representation
|
|
41
43
|
message.to_json # => JSON string
|
|
42
44
|
```
|
|
43
45
|
|
|
46
|
+
Type and role predicates:
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
message.text? # => true if type is "text"
|
|
50
|
+
message.tool_call? # => true if type is "tool_call"
|
|
51
|
+
message.tool_result? # => true if type is "tool_result"
|
|
52
|
+
message.system? # => true if role is "system"
|
|
53
|
+
message.user? # => true if role is "user"
|
|
54
|
+
message.assistant? # => true if role is "assistant"
|
|
55
|
+
message.stopped? # => true if stop_reason is "stop"
|
|
56
|
+
message.tool_stop? # => true if stop_reason is "tool"
|
|
57
|
+
```
|
|
58
|
+
|
|
44
59
|
## Classes
|
|
45
60
|
|
|
46
61
|
| Class | Description |
|
|
47
62
|
|-------|-------------|
|
|
48
|
-
| [UserMessage](user-message.md) | User input with
|
|
49
|
-
| [TextMessage](text-message.md) |
|
|
50
|
-
| [ToolCallMessage](tool-call-message.md) | Tool invocation request |
|
|
63
|
+
| [UserMessage](user-message.md) | User input with session and metadata |
|
|
64
|
+
| [TextMessage](text-message.md) | Text message with role (system, user, or assistant) |
|
|
65
|
+
| [ToolCallMessage](tool-call-message.md) | Tool invocation request containing ToolMessage objects |
|
|
51
66
|
| [ToolResultMessage](tool-result-message.md) | Tool execution result |
|
|
52
67
|
|
|
53
|
-
## Usage in
|
|
68
|
+
## Usage in Memory
|
|
54
69
|
|
|
55
|
-
Messages are typically accessed through
|
|
70
|
+
Messages are typically accessed through memory:
|
|
56
71
|
|
|
57
72
|
```ruby
|
|
58
|
-
|
|
73
|
+
memory.messages # => Array<Message>
|
|
59
74
|
|
|
60
75
|
# Format for LLM
|
|
61
|
-
|
|
76
|
+
memory.format_history # => Array<Message>
|
|
62
77
|
```
|
|
63
78
|
|
|
64
79
|
## See Also
|
|
65
80
|
|
|
66
|
-
- [
|
|
81
|
+
- [Memory](../core/memory.md)
|
|
67
82
|
- [Message Flow Architecture](../../architecture/message-flow.md)
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
# TextMessage
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Text message from system, user, or assistant.
|
|
4
4
|
|
|
5
5
|
## Class: `RobotLab::TextMessage`
|
|
6
6
|
|
|
7
7
|
```ruby
|
|
8
|
-
message = TextMessage.new("Hello! How can I help you today?")
|
|
8
|
+
message = TextMessage.new(role: "assistant", content: "Hello! How can I help you today?")
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Constructor
|
|
12
12
|
|
|
13
13
|
```ruby
|
|
14
|
-
TextMessage.new(content)
|
|
14
|
+
TextMessage.new(role:, content:, stop_reason: nil)
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
**Parameters:**
|
|
18
18
|
|
|
19
19
|
| Name | Type | Description |
|
|
20
20
|
|------|------|-------------|
|
|
21
|
-
| `
|
|
21
|
+
| `role` | `String` | Message role ("system", "user", or "assistant") |
|
|
22
|
+
| `content` | `String` | The text content |
|
|
23
|
+
| `stop_reason` | `String`, `nil` | Stop reason ("stop" or "tool") |
|
|
22
24
|
|
|
23
25
|
## Attributes
|
|
24
26
|
|
|
@@ -28,15 +30,31 @@ TextMessage.new(content)
|
|
|
28
30
|
message.content # => String
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
The
|
|
33
|
+
The text content.
|
|
32
34
|
|
|
33
35
|
### role
|
|
34
36
|
|
|
35
37
|
```ruby
|
|
36
|
-
message.role # =>
|
|
38
|
+
message.role # => "assistant"
|
|
37
39
|
```
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
Returns a String: `"system"`, `"user"`, or `"assistant"`.
|
|
42
|
+
|
|
43
|
+
### type
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
message.type # => "text"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Always returns `"text"`.
|
|
50
|
+
|
|
51
|
+
### stop_reason
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
message.stop_reason # => "stop" or nil
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The stop reason, if any.
|
|
40
58
|
|
|
41
59
|
## Methods
|
|
42
60
|
|
|
@@ -52,8 +70,10 @@ Hash representation.
|
|
|
52
70
|
|
|
53
71
|
```ruby
|
|
54
72
|
{
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
type: "text",
|
|
74
|
+
role: "assistant",
|
|
75
|
+
content: "Hello! How can I help you today?",
|
|
76
|
+
stop_reason: "stop"
|
|
57
77
|
}
|
|
58
78
|
```
|
|
59
79
|
|
|
@@ -65,34 +85,60 @@ message.to_json # => String
|
|
|
65
85
|
|
|
66
86
|
JSON representation.
|
|
67
87
|
|
|
88
|
+
### Predicates
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
message.text? # => true
|
|
92
|
+
message.tool_call? # => false
|
|
93
|
+
message.assistant? # => true (if role is "assistant")
|
|
94
|
+
message.user? # => false
|
|
95
|
+
message.stopped? # => true (if stop_reason is "stop")
|
|
96
|
+
```
|
|
97
|
+
|
|
68
98
|
## Examples
|
|
69
99
|
|
|
70
|
-
###
|
|
100
|
+
### System Message
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
message = TextMessage.new(role: "system", content: "You are a helpful assistant")
|
|
104
|
+
message.system? # => true
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### User Message
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
message = TextMessage.new(role: "user", content: "What's the weather?")
|
|
111
|
+
message.user? # => true
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Assistant Response
|
|
71
115
|
|
|
72
116
|
```ruby
|
|
73
|
-
message = TextMessage.new(
|
|
117
|
+
message = TextMessage.new(
|
|
118
|
+
role: "assistant",
|
|
119
|
+
content: "Your order has shipped!",
|
|
120
|
+
stop_reason: "stop"
|
|
121
|
+
)
|
|
122
|
+
message.assistant? # => true
|
|
123
|
+
message.stopped? # => true
|
|
74
124
|
```
|
|
75
125
|
|
|
76
126
|
### In Robot Results
|
|
77
127
|
|
|
78
128
|
```ruby
|
|
79
|
-
result = robot.run(
|
|
129
|
+
result = robot.run("Tell me a joke")
|
|
80
130
|
|
|
81
|
-
#
|
|
82
|
-
result.
|
|
83
|
-
|
|
84
|
-
puts msg.content
|
|
85
|
-
end
|
|
131
|
+
# The result is a TextMessage when the assistant replies with text
|
|
132
|
+
if result.text?
|
|
133
|
+
puts result.content
|
|
86
134
|
end
|
|
87
135
|
```
|
|
88
136
|
|
|
89
137
|
### Filtering Text Content
|
|
90
138
|
|
|
91
139
|
```ruby
|
|
92
|
-
# Get only text
|
|
93
|
-
|
|
94
|
-
msg.is_a?(TextMessage)
|
|
95
|
-
end.map(&:content)
|
|
140
|
+
# Get only text messages from memory
|
|
141
|
+
text_messages = memory.messages.select(&:text?).map(&:content)
|
|
96
142
|
```
|
|
97
143
|
|
|
98
144
|
## See Also
|