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/api/mcp/server.md
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
# MCP Server Configuration
|
|
2
|
-
|
|
3
|
-
Data structure for MCP server connection configuration.
|
|
4
|
-
|
|
5
|
-
## Class: `RobotLab::MCP::Server`
|
|
6
|
-
|
|
7
|
-
`Server` is a configuration object that defines how to connect to an MCP server. It holds the server name and transport settings, and validates the configuration on construction.
|
|
8
|
-
|
|
9
|
-
This is **not** an MCP server implementation -- it is the configuration used by `MCP::Client` to establish a connection to an external MCP server.
|
|
10
|
-
|
|
11
|
-
```ruby
|
|
12
|
-
server = RobotLab::MCP::Server.new(
|
|
13
|
-
name: "filesystem",
|
|
14
|
-
description: "Read, write, and search local files and directories",
|
|
15
|
-
transport: { type: "stdio", command: "mcp-server-filesystem", args: ["--root", "/data"] }
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
# With custom timeout
|
|
19
|
-
server = RobotLab::MCP::Server.new(
|
|
20
|
-
name: "slow_server",
|
|
21
|
-
transport: { type: "stdio", command: "heavy-mcp-server" },
|
|
22
|
-
timeout: 30
|
|
23
|
-
)
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Constructor
|
|
27
|
-
|
|
28
|
-
```ruby
|
|
29
|
-
Server.new(name:, transport:, timeout: nil, description: nil, **_extra)
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
**Parameters:**
|
|
33
|
-
|
|
34
|
-
| Name | Type | Default | Description |
|
|
35
|
-
|------|------|---------|-------------|
|
|
36
|
-
| `name` | `String` | **required** | Unique server identifier |
|
|
37
|
-
| `transport` | `Hash` | **required** | Transport configuration (must include `type`) |
|
|
38
|
-
| `timeout` | `Numeric`, `nil` | `15` | Request timeout in seconds. Values >= 1000 are auto-converted from milliseconds. Minimum 1 second |
|
|
39
|
-
| `description` | `String`, `nil` | `""` | Human-readable summary of what the server does; scored by `MCP::ServerDiscovery` |
|
|
40
|
-
|
|
41
|
-
Any other keyword is absorbed by `**_extra` and silently discarded, so a
|
|
42
|
-
misspelled key raises nothing.
|
|
43
|
-
|
|
44
|
-
**Raises:** `ArgumentError` if:
|
|
45
|
-
- The transport type is not one of the valid types
|
|
46
|
-
- A stdio transport is missing the `:command` key
|
|
47
|
-
- A network transport (ws, websocket, sse, streamable-http, http) is missing the `:url` key
|
|
48
|
-
|
|
49
|
-
## Constants
|
|
50
|
-
|
|
51
|
-
```ruby
|
|
52
|
-
RobotLab::MCP::Server::VALID_TRANSPORT_TYPES
|
|
53
|
-
# => ["stdio", "sse", "ws", "websocket", "streamable-http", "http"]
|
|
54
|
-
|
|
55
|
-
RobotLab::MCP::Server::DEFAULT_TIMEOUT
|
|
56
|
-
# => 15 (seconds)
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Attributes
|
|
60
|
-
|
|
61
|
-
### name
|
|
62
|
-
|
|
63
|
-
```ruby
|
|
64
|
-
server.name # => String
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
The server identifier string.
|
|
68
|
-
|
|
69
|
-
### description
|
|
70
|
-
|
|
71
|
-
```ruby
|
|
72
|
-
server.description # => String
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
Human-readable summary of the server's capabilities. Always a String — a `nil`
|
|
76
|
-
or omitted `description:` becomes `""`. Used by `MCP::ServerDiscovery`, which
|
|
77
|
-
scores `"#{name} #{description}"` against the user's message when a robot is
|
|
78
|
-
built with `mcp_discovery: true`. Discovery falls back to the full server list
|
|
79
|
-
when no server in the list has a non-empty description.
|
|
80
|
-
|
|
81
|
-
### transport
|
|
82
|
-
|
|
83
|
-
```ruby
|
|
84
|
-
server.transport # => Hash
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
The normalized transport configuration hash (keys are symbols, type is downcased).
|
|
88
|
-
|
|
89
|
-
### timeout
|
|
90
|
-
|
|
91
|
-
```ruby
|
|
92
|
-
server.timeout # => Numeric
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Request timeout in seconds. Defaults to `DEFAULT_TIMEOUT` (15). Values >= 1000 passed to the constructor are auto-converted from milliseconds to seconds. The minimum is 1 second.
|
|
96
|
-
|
|
97
|
-
## Methods
|
|
98
|
-
|
|
99
|
-
### transport_type
|
|
100
|
-
|
|
101
|
-
```ruby
|
|
102
|
-
server.transport_type # => String
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Returns the transport type string (e.g., `"stdio"`, `"ws"`, `"sse"`).
|
|
106
|
-
|
|
107
|
-
### to_h
|
|
108
|
-
|
|
109
|
-
```ruby
|
|
110
|
-
server.to_h
|
|
111
|
-
# => { name: "y", description: "", transport: { type: "sse", url: "http://x" }, timeout: 30.0 }
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Converts the server configuration to a hash representation. The hash is **not**
|
|
115
|
-
compacted — all four keys are always present, and `description` is `""` when it
|
|
116
|
-
was never set. Note that `timeout` is normalized to a Float unless it defaulted
|
|
117
|
-
(`DEFAULT_TIMEOUT` is the Integer `15`).
|
|
118
|
-
|
|
119
|
-
## Transport Configuration Options
|
|
120
|
-
|
|
121
|
-
### Stdio Transport
|
|
122
|
-
|
|
123
|
-
For local MCP servers running as subprocesses:
|
|
124
|
-
|
|
125
|
-
```ruby
|
|
126
|
-
Server.new(
|
|
127
|
-
name: "filesystem",
|
|
128
|
-
transport: {
|
|
129
|
-
type: "stdio",
|
|
130
|
-
command: "mcp-server-filesystem", # Required
|
|
131
|
-
args: ["--root", "/data"], # Optional
|
|
132
|
-
env: { "DEBUG" => "true" } # Optional
|
|
133
|
-
}
|
|
134
|
-
)
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
| Key | Type | Required | Description |
|
|
138
|
-
|-----|------|----------|-------------|
|
|
139
|
-
| `type` | `String` | Yes | Must be `"stdio"` |
|
|
140
|
-
| `command` | `String` | Yes | Executable command |
|
|
141
|
-
| `args` | `Array<String>` | No | Command arguments |
|
|
142
|
-
| `env` | `Hash` | No | Environment variables |
|
|
143
|
-
|
|
144
|
-
### WebSocket Transport
|
|
145
|
-
|
|
146
|
-
For bidirectional real-time communication:
|
|
147
|
-
|
|
148
|
-
```ruby
|
|
149
|
-
Server.new(
|
|
150
|
-
name: "neon",
|
|
151
|
-
transport: {
|
|
152
|
-
type: "ws",
|
|
153
|
-
url: "ws://localhost:8080" # Required
|
|
154
|
-
}
|
|
155
|
-
)
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
| Key | Type | Required | Description |
|
|
159
|
-
|-----|------|----------|-------------|
|
|
160
|
-
| `type` | `String` | Yes | `"ws"` or `"websocket"` |
|
|
161
|
-
| `url` | `String` | Yes | WebSocket endpoint URL |
|
|
162
|
-
|
|
163
|
-
### SSE Transport
|
|
164
|
-
|
|
165
|
-
For server-sent events streaming:
|
|
166
|
-
|
|
167
|
-
```ruby
|
|
168
|
-
Server.new(
|
|
169
|
-
name: "streaming",
|
|
170
|
-
transport: {
|
|
171
|
-
type: "sse",
|
|
172
|
-
url: "http://localhost:8080/sse" # Required
|
|
173
|
-
}
|
|
174
|
-
)
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
| Key | Type | Required | Description |
|
|
178
|
-
|-----|------|----------|-------------|
|
|
179
|
-
| `type` | `String` | Yes | Must be `"sse"` |
|
|
180
|
-
| `url` | `String` | Yes | SSE endpoint URL |
|
|
181
|
-
|
|
182
|
-
### Streamable HTTP Transport
|
|
183
|
-
|
|
184
|
-
For HTTP-based communication with session management:
|
|
185
|
-
|
|
186
|
-
```ruby
|
|
187
|
-
Server.new(
|
|
188
|
-
name: "api",
|
|
189
|
-
transport: {
|
|
190
|
-
type: "streamable-http",
|
|
191
|
-
url: "https://server.smithery.ai/neon/mcp", # Required
|
|
192
|
-
session_id: "abc123", # Optional
|
|
193
|
-
auth_provider: -> { "Bearer #{token}" } # Optional
|
|
194
|
-
}
|
|
195
|
-
)
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
| Key | Type | Required | Description |
|
|
199
|
-
|-----|------|----------|-------------|
|
|
200
|
-
| `type` | `String` | Yes | `"streamable-http"` or `"http"` |
|
|
201
|
-
| `url` | `String` | Yes | HTTP endpoint URL |
|
|
202
|
-
| `session_id` | `String` | No | Session identifier |
|
|
203
|
-
| `auth_provider` | `Proc` | No | Authentication callback returning auth header value |
|
|
204
|
-
|
|
205
|
-
## Examples
|
|
206
|
-
|
|
207
|
-
### Multiple Server Configurations
|
|
208
|
-
|
|
209
|
-
```ruby
|
|
210
|
-
servers = [
|
|
211
|
-
{
|
|
212
|
-
name: "filesystem",
|
|
213
|
-
transport: { type: "stdio", command: "mcp-server-filesystem", args: ["/data"] }
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
name: "github",
|
|
217
|
-
transport: { type: "stdio", command: "mcp-server-github" }
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
name: "database",
|
|
221
|
-
transport: { type: "ws", url: "ws://localhost:9090" }
|
|
222
|
-
}
|
|
223
|
-
]
|
|
224
|
-
|
|
225
|
-
# Pass directly to a robot
|
|
226
|
-
robot = Robot.new(
|
|
227
|
-
name: "dev_assistant",
|
|
228
|
-
system_prompt: "You help with development tasks.",
|
|
229
|
-
mcp: servers
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
# `run` defaults to `mcp: :none, tools: :none` — opt in per run
|
|
233
|
-
robot.run("What changed in the repo?", mcp: :inherit, tools: :inherit)
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### Creating a Client from a Server
|
|
237
|
-
|
|
238
|
-
```ruby
|
|
239
|
-
server = RobotLab::MCP::Server.new(
|
|
240
|
-
name: "tools",
|
|
241
|
-
transport: { type: "ws", url: "ws://localhost:8080" }
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
client = RobotLab::MCP::Client.new(server)
|
|
245
|
-
client.connect
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
## See Also
|
|
249
|
-
|
|
250
|
-
- [MCP Overview](index.md)
|
|
251
|
-
- [MCP Client](client.md)
|
|
252
|
-
- [Transports](transports.md)
|
data/docs/api/mcp/transports.md
DELETED
|
@@ -1,339 +0,0 @@
|
|
|
1
|
-
# MCP Transports
|
|
2
|
-
|
|
3
|
-
Communication methods for MCP client-server connections.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
RobotLab provides four built-in transport types:
|
|
10
|
-
|
|
11
|
-
| Transport | Class | Use Case | Status |
|
|
12
|
-
|-----------|-------|----------|--------|
|
|
13
|
-
| Stdio | `Transports::Stdio` | Local subprocess servers | Fully working |
|
|
14
|
-
| WebSocket | `Transports::WebSocket` | Real-time bidirectional | **Broken** — see below |
|
|
15
|
-
| SSE | `Transports::SSE` | Server-sent events | Working, but `connect` reports success unconditionally |
|
|
16
|
-
| Streamable HTTP | `Transports::StreamableHTTP` | HTTP with session support | Working, but `connect` reports success unconditionally |
|
|
17
|
-
|
|
18
|
-
> **Read this before using a non-stdio transport.** Only `Stdio` performs its
|
|
19
|
-
> connection and MCP handshake synchronously. The other three wrap that work in
|
|
20
|
-
> an `Async do ... end` block whose result is never awaited, so any error raised
|
|
21
|
-
> inside — including a refused TCP connection — is discarded. `connect` returns
|
|
22
|
-
> `self` regardless. The specific consequences are documented per transport
|
|
23
|
-
> below.
|
|
24
|
-
|
|
25
|
-
## Base Interface
|
|
26
|
-
|
|
27
|
-
All transports inherit from `RobotLab::MCP::Transports::Base` and implement:
|
|
28
|
-
|
|
29
|
-
```ruby
|
|
30
|
-
class RobotLab::MCP::Transports::Base
|
|
31
|
-
DEFAULT_TIMEOUT = 15 # seconds
|
|
32
|
-
|
|
33
|
-
attr_reader :config # => Hash (symbolized keys, :timeout removed)
|
|
34
|
-
attr_reader :timeout # => Numeric (seconds, extracted from config)
|
|
35
|
-
|
|
36
|
-
def connect # Establish connection, returns self
|
|
37
|
-
def send_request(message) # Send JSON-RPC message, returns Hash response
|
|
38
|
-
def close # Close connection, returns self
|
|
39
|
-
def connected? # Returns Boolean
|
|
40
|
-
end
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
The `timeout` is extracted from the config hash during initialization (and removed from `config`). If not provided, it defaults to `DEFAULT_TIMEOUT` (15 seconds). The timeout is propagated from `MCP::Server` through `MCP::Client` to the transport.
|
|
44
|
-
|
|
45
|
-
**Only `Stdio` enforces the timeout.** `SSE`, `WebSocket`, and `StreamableHTTP`
|
|
46
|
-
store `@timeout` and expose it through the `timeout` reader, but never reference
|
|
47
|
-
it — their requests are not time-bounded by this value. If you need a bound on a
|
|
48
|
-
non-stdio transport, wrap the call yourself, or route the client through an
|
|
49
|
-
`MCP::ConnectionPoller` (stdio only).
|
|
50
|
-
|
|
51
|
-
## Stdio Transport
|
|
52
|
-
|
|
53
|
-
**Class:** `RobotLab::MCP::Transports::Stdio`
|
|
54
|
-
|
|
55
|
-
Spawns a subprocess and communicates via stdin/stdout using JSON-RPC messages (one per line). Automatically sends MCP `initialize` and `notifications/initialized` on connect. All blocking I/O is wrapped with `Timeout.timeout` so a missing or hung server cannot block the caller forever.
|
|
56
|
-
|
|
57
|
-
### Configuration
|
|
58
|
-
|
|
59
|
-
```ruby
|
|
60
|
-
{
|
|
61
|
-
type: "stdio",
|
|
62
|
-
command: "mcp-server-filesystem", # Required: executable command
|
|
63
|
-
args: ["--root", "/data"], # Optional: command arguments
|
|
64
|
-
env: { "DEBUG" => "true" }, # Optional: environment variables
|
|
65
|
-
timeout: 10 # Optional: request timeout in seconds (default: 15)
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
| Key | Type | Required | Description |
|
|
70
|
-
|-----|------|----------|-------------|
|
|
71
|
-
| `command` | `String` | Yes | Executable command to spawn |
|
|
72
|
-
| `args` | `Array<String>` | No | Command arguments |
|
|
73
|
-
| `env` | `Hash` | No | Environment variables (merged with current env) |
|
|
74
|
-
| `timeout` | `Numeric` | No | Request timeout in seconds (default: 15) |
|
|
75
|
-
|
|
76
|
-
### Behavior
|
|
77
|
-
|
|
78
|
-
- Uses `Open3.popen3` to spawn the subprocess
|
|
79
|
-
- Verifies the process actually started (raises `MCPError` if it exits immediately)
|
|
80
|
-
- Writes JSON-RPC messages to stdin (one per line)
|
|
81
|
-
- Reads responses from stdout, skipping notifications (messages without `id`)
|
|
82
|
-
- All blocking reads are wrapped with `Timeout.timeout` — raises `MCPError` if the server does not respond within the timeout period
|
|
83
|
-
- `connected?` returns `true` when the subprocess is alive
|
|
84
|
-
- `close` calls `cleanup_process` to reliably close stdin, stdout, stderr and kill the subprocess
|
|
85
|
-
- Handles `Errno::ENOENT` (command not found), `Errno::EPIPE` / `IOError` (broken pipe / connection lost), and `Timeout::Error` (hung server) with clear error messages
|
|
86
|
-
|
|
87
|
-
### Example
|
|
88
|
-
|
|
89
|
-
```ruby
|
|
90
|
-
transport = RobotLab::MCP::Transports::Stdio.new(
|
|
91
|
-
command: "mcp-server-filesystem",
|
|
92
|
-
args: ["--root", "/data"],
|
|
93
|
-
env: { "DEBUG" => "true" },
|
|
94
|
-
timeout: 10
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
transport.connect
|
|
98
|
-
response = transport.send_request({ jsonrpc: "2.0", id: 1, method: "tools/list" })
|
|
99
|
-
transport.close
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## WebSocket Transport
|
|
103
|
-
|
|
104
|
-
**Class:** `RobotLab::MCP::Transports::WebSocket`
|
|
105
|
-
|
|
106
|
-
Intended to use `async-websocket` for non-blocking bidirectional communication.
|
|
107
|
-
|
|
108
|
-
> **This transport does not currently work.** `connect` calls
|
|
109
|
-
> `Async::HTTP::Endpoint.parse`, but only requires `async` and
|
|
110
|
-
> `async/websocket/client` — the `Async::HTTP` namespace is never loaded, so the
|
|
111
|
-
> call raises `NameError`. That happens inside an un-awaited `Async` block, so
|
|
112
|
-
> the error is swallowed: `connect` returns `self`, no `MCPError` is raised, and
|
|
113
|
-
> `connected?` stays `false`. Every subsequent `send_request` then raises
|
|
114
|
-
> `MCPError, "Not connected"`.
|
|
115
|
-
>
|
|
116
|
-
> Verified: `WebSocket.new(url: "ws://127.0.0.1:9/ws").connect.connected?` is
|
|
117
|
-
> `false` with no exception surfacing.
|
|
118
|
-
>
|
|
119
|
-
> The rescue on `connect` only catches `LoadError`, which is raised if
|
|
120
|
-
> `async-websocket` is missing. Because of the `Async::HTTP::Endpoint` call, this
|
|
121
|
-
> transport also needs `async-http` even once `async-websocket` is installed —
|
|
122
|
-
> both are declared as runtime dependencies of the gem.
|
|
123
|
-
|
|
124
|
-
### Configuration
|
|
125
|
-
|
|
126
|
-
```ruby
|
|
127
|
-
{
|
|
128
|
-
type: "ws", # or "websocket"
|
|
129
|
-
url: "wss://mcp.example.com/ws" # Required: WebSocket endpoint
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
| Key | Type | Required | Description |
|
|
134
|
-
|-----|------|----------|-------------|
|
|
135
|
-
| `url` | `String` | Yes | WebSocket endpoint URL |
|
|
136
|
-
|
|
137
|
-
### Behavior
|
|
138
|
-
|
|
139
|
-
- Intends to use `Async::WebSocket::Client.connect` within an `Async` block, then
|
|
140
|
-
send the MCP `initialize` handshake
|
|
141
|
-
- In practice the block raises `NameError` on `Async::HTTP::Endpoint` before the
|
|
142
|
-
connection is created, and the un-awaited block discards it
|
|
143
|
-
- `send_request` sends JSON-RPC messages as JSON strings and reads the response
|
|
144
|
-
inside an awaited `Async` block — but it raises `MCPError, "Not connected"`
|
|
145
|
-
because `@connected` was never set
|
|
146
|
-
- `connect` raises `MCPError` only for `LoadError` (missing `async-websocket`);
|
|
147
|
-
it does not raise for a connection failure
|
|
148
|
-
- `close` is a no-op while `@connected` is `false`
|
|
149
|
-
|
|
150
|
-
### Example
|
|
151
|
-
|
|
152
|
-
```ruby
|
|
153
|
-
transport = RobotLab::MCP::Transports::WebSocket.new(
|
|
154
|
-
url: "ws://localhost:8080"
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
transport.connect
|
|
158
|
-
transport.connected? # => false, even against a live server
|
|
159
|
-
|
|
160
|
-
# Raises MCPError: "Not connected"
|
|
161
|
-
transport.send_request({ jsonrpc: "2.0", id: 1, method: "tools/list" })
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
## SSE Transport
|
|
165
|
-
|
|
166
|
-
**Class:** `RobotLab::MCP::Transports::SSE`
|
|
167
|
-
|
|
168
|
-
Uses `async-http` for HTTP-based communication. Sends requests via HTTP POST and reads responses. Requires the `async-http` gem.
|
|
169
|
-
|
|
170
|
-
### Configuration
|
|
171
|
-
|
|
172
|
-
```ruby
|
|
173
|
-
{
|
|
174
|
-
type: "sse",
|
|
175
|
-
url: "http://localhost:8080/sse" # Required: SSE endpoint
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
| Key | Type | Required | Description |
|
|
180
|
-
|-----|------|----------|-------------|
|
|
181
|
-
| `url` | `String` | Yes | SSE/HTTP endpoint URL |
|
|
182
|
-
|
|
183
|
-
### Behavior
|
|
184
|
-
|
|
185
|
-
- Creates an `Async::HTTP::Client` on connect, then sends the MCP `initialize`
|
|
186
|
-
handshake
|
|
187
|
-
- Sends JSON-RPC messages via HTTP POST with `Content-Type: application/json`
|
|
188
|
-
- Reads and parses JSON response body
|
|
189
|
-
- Raises `MCPError` if the `async-http` gem is not installed (`LoadError` only)
|
|
190
|
-
- The `timeout` from the server config is stored but never applied
|
|
191
|
-
|
|
192
|
-
> **`connect` always reports success.** `@connected = true` is assigned *before*
|
|
193
|
-
> `send_initialize` runs, and the whole sequence is inside an un-awaited `Async`
|
|
194
|
-
> block. Against an unreachable host, `connect` returns `self`, `connected?`
|
|
195
|
-
> returns `true`, and the handshake failure is never surfaced. The first real
|
|
196
|
-
> `send_request` is where the failure appears.
|
|
197
|
-
>
|
|
198
|
-
> Verified: `SSE.new(url: "http://127.0.0.1:9/sse").connect.connected?` is `true`.
|
|
199
|
-
|
|
200
|
-
### Example
|
|
201
|
-
|
|
202
|
-
```ruby
|
|
203
|
-
transport = RobotLab::MCP::Transports::SSE.new(
|
|
204
|
-
url: "http://localhost:8080/sse"
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
transport.connect
|
|
208
|
-
# connected? is true here whether or not the server exists
|
|
209
|
-
|
|
210
|
-
response = transport.send_request({ jsonrpc: "2.0", id: 1, method: "tools/list" })
|
|
211
|
-
transport.close
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
## Streamable HTTP Transport
|
|
215
|
-
|
|
216
|
-
**Class:** `RobotLab::MCP::Transports::StreamableHTTP`
|
|
217
|
-
|
|
218
|
-
HTTP-based transport with session management and optional authentication. Supports session IDs for maintaining server-side state across requests. Requires the `async-http` gem.
|
|
219
|
-
|
|
220
|
-
### Configuration
|
|
221
|
-
|
|
222
|
-
```ruby
|
|
223
|
-
{
|
|
224
|
-
type: "streamable-http", # or "http"
|
|
225
|
-
url: "https://server.smithery.ai/neon/mcp", # Required: HTTP endpoint
|
|
226
|
-
session_id: "abc123", # Optional: session identifier
|
|
227
|
-
auth_provider: -> { "Bearer #{token}" } # Optional: auth callback
|
|
228
|
-
}
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
| Key | Type | Required | Description |
|
|
232
|
-
|-----|------|----------|-------------|
|
|
233
|
-
| `url` | `String` | Yes | HTTP endpoint URL |
|
|
234
|
-
| `session_id` | `String` | No | Pre-existing session identifier |
|
|
235
|
-
| `auth_provider` | `Proc` | No | Callback returning Authorization header value |
|
|
236
|
-
|
|
237
|
-
### Behavior
|
|
238
|
-
|
|
239
|
-
- Creates an `Async::HTTP::Client` on connect
|
|
240
|
-
- Sends MCP `initialize` on connect; if no session ID was provided, extracts it from the server response (`serverInfo.sessionId`)
|
|
241
|
-
- Sends `X-Session-ID` header with each request when a session ID is available
|
|
242
|
-
- Calls `auth_provider` for each request to populate the `Authorization` header
|
|
243
|
-
- Exposes `session_id` reader for accessing the current session ID
|
|
244
|
-
- Raises `MCPError` if the `async-http` gem is not installed (`LoadError` only)
|
|
245
|
-
- The `timeout` from the server config is stored but never applied
|
|
246
|
-
|
|
247
|
-
> **`connect` always reports success**, for the same reason as SSE:
|
|
248
|
-
> `@connected = true` precedes `send_initialize`, and the enclosing `Async` block
|
|
249
|
-
> is never awaited. `connected?` returns `true` against an unreachable host, and
|
|
250
|
-
> `session_id` stays at whatever you configured (`nil` if you configured nothing)
|
|
251
|
-
> because the handshake result was discarded.
|
|
252
|
-
>
|
|
253
|
-
> Verified: `StreamableHTTP.new(url: "http://127.0.0.1:9/mcp").connect` yields
|
|
254
|
-
> `connected? == true`, `session_id == nil`.
|
|
255
|
-
|
|
256
|
-
### Example
|
|
257
|
-
|
|
258
|
-
```ruby
|
|
259
|
-
transport = RobotLab::MCP::Transports::StreamableHTTP.new(
|
|
260
|
-
url: "https://server.smithery.ai/neon/mcp",
|
|
261
|
-
auth_provider: -> { "Bearer #{ENV['MCP_TOKEN']}" }
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
transport.connect
|
|
265
|
-
puts transport.session_id # => pre-configured value, or nil until a request lands
|
|
266
|
-
|
|
267
|
-
response = transport.send_request({ jsonrpc: "2.0", id: 1, method: "tools/list" })
|
|
268
|
-
transport.close
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
## Connection Lifecycle
|
|
272
|
-
|
|
273
|
-
All transports expose the same four-step lifecycle:
|
|
274
|
-
|
|
275
|
-
1. **Create** -- instantiate with configuration hash
|
|
276
|
-
2. **Connect** -- establish connection and perform MCP protocol initialization
|
|
277
|
-
3. **Request/Response** -- send JSON-RPC requests, receive responses
|
|
278
|
-
4. **Close** -- tear down connection and release resources
|
|
279
|
-
|
|
280
|
-
Step 2 behaves differently per transport:
|
|
281
|
-
|
|
282
|
-
| Transport | `connect` is synchronous | Errors surface from `connect` | `connected?` reflects reality |
|
|
283
|
-
|-----------|--------------------------|-------------------------------|-------------------------------|
|
|
284
|
-
| `Stdio` | Yes | Yes (`MCPError`) | Yes — also checks the process is alive |
|
|
285
|
-
| `SSE` | No (un-awaited `Async`) | No | No — always `true` after `connect` |
|
|
286
|
-
| `StreamableHTTP` | No (un-awaited `Async`) | No | No — always `true` after `connect` |
|
|
287
|
-
| `WebSocket` | No (un-awaited `Async`) | No | No — always `false` after `connect` |
|
|
288
|
-
|
|
289
|
-
Only `Stdio` gives you a trustworthy answer at connect time. For the other three,
|
|
290
|
-
treat the first `send_request` as the real connection test.
|
|
291
|
-
|
|
292
|
-
Each transport builds the same MCP `initialize` message during connect:
|
|
293
|
-
|
|
294
|
-
```json
|
|
295
|
-
{
|
|
296
|
-
"jsonrpc": "2.0",
|
|
297
|
-
"id": 0,
|
|
298
|
-
"method": "initialize",
|
|
299
|
-
"params": {
|
|
300
|
-
"protocolVersion": "2024-11-05",
|
|
301
|
-
"capabilities": {},
|
|
302
|
-
"clientInfo": {
|
|
303
|
-
"name": "RobotLab",
|
|
304
|
-
"version": "<current version>"
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
## Error Handling
|
|
311
|
-
|
|
312
|
-
All transports raise `RobotLab::MCPError` for connection and communication failures:
|
|
313
|
-
|
|
314
|
-
```ruby
|
|
315
|
-
begin
|
|
316
|
-
transport.connect
|
|
317
|
-
transport.send_request(message)
|
|
318
|
-
rescue RobotLab::MCPError => e
|
|
319
|
-
puts "Transport error: #{e.message}"
|
|
320
|
-
ensure
|
|
321
|
-
transport.close
|
|
322
|
-
end
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
Specific error cases:
|
|
326
|
-
- **Not connected** -- calling `send_request` before `connect` raises `MCPError` (all transports)
|
|
327
|
-
- **Missing gem** -- WebSocket, SSE, and HTTP transports raise `MCPError` with a `LoadError` message if the required gem (`async-websocket` / `async-http`) is not installed. This is the *only* error `connect` re-raises on those three
|
|
328
|
-
- **Connection refused / unreachable host** -- **not** reported by SSE, WebSocket, or StreamableHTTP `connect`; the error is discarded with the un-awaited `Async` block
|
|
329
|
-
- **No response** -- Stdio transport raises `MCPError` if the subprocess produces no output (EOF on stdout)
|
|
330
|
-
- **Command not found** -- Stdio transport raises `MCPError` with the original `Errno::ENOENT` message
|
|
331
|
-
- **Timeout** -- Stdio transport raises `MCPError` if the server does not respond within the configured timeout. No other transport enforces a timeout
|
|
332
|
-
- **Broken pipe** -- Stdio transport raises `MCPError` and marks itself disconnected on `Errno::EPIPE` or `IOError`
|
|
333
|
-
- **Immediate exit** -- Stdio transport raises `MCPError` if the server process exits immediately after spawn
|
|
334
|
-
|
|
335
|
-
## See Also
|
|
336
|
-
|
|
337
|
-
- [MCP Overview](index.md)
|
|
338
|
-
- [MCP Client](client.md)
|
|
339
|
-
- [Server Configuration](server.md)
|