little_ghost 0.6.0 → 0.8.0

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -8
  3. data/docs/guides/code_mode.md +4 -2
  4. data/docs/guides/getting_started.md +26 -1
  5. data/docs/guides/integrations.md +20 -161
  6. data/docs/guides/mcp.md +288 -0
  7. data/docs/guides/models_and_providers.md +62 -4
  8. data/docs/guides/production.md +14 -5
  9. data/docs/guides/providers.md +275 -0
  10. data/docs/guides/sandboxing.md +42 -7
  11. data/docs/guides/skills.md +1 -3
  12. data/docs/guides/tools.md +2 -0
  13. data/exe/little_ghost +12 -0
  14. data/lib/little_ghost/assembly.rb +11 -3
  15. data/lib/little_ghost/assembly_builder.rb +2 -2
  16. data/lib/little_ghost/cli.rb +138 -0
  17. data/lib/little_ghost/code_mode/ruby/host.rb +50 -10
  18. data/lib/little_ghost/code_mode/ruby/session.rb +4 -0
  19. data/lib/little_ghost/code_mode/ruby_engine.rb +1 -0
  20. data/lib/little_ghost/console.rb +17 -0
  21. data/lib/little_ghost/embeddings/request.rb +103 -0
  22. data/lib/little_ghost/embeddings/response.rb +55 -0
  23. data/lib/little_ghost/errors.rb +3 -1
  24. data/lib/little_ghost/generators/application_generator.rb +179 -0
  25. data/lib/little_ghost/generators/templates/application/Gemfile.tt +3 -0
  26. data/lib/little_ghost/generators/templates/application/README.md.tt +29 -0
  27. data/lib/little_ghost/generators/templates/application/app/agents/application_agent.rb.tt +5 -0
  28. data/lib/little_ghost/generators/templates/application/app/prompts/application/system.erb.tt +1 -0
  29. data/lib/little_ghost/generators/templates/application/bin/application.tt +10 -0
  30. data/lib/little_ghost/generators/templates/application/config/little_ghost.rb.tt +5 -0
  31. data/lib/little_ghost/generators/templates/application/gitignore.tt +2 -0
  32. data/lib/little_ghost/generators/templates/application/keep.tt +0 -0
  33. data/lib/little_ghost/mcp/client.rb +295 -606
  34. data/lib/little_ghost/mcp/toolset.rb +133 -95
  35. data/lib/little_ghost/mcp.rb +21 -3
  36. data/lib/little_ghost/model.rb +20 -0
  37. data/lib/little_ghost/model_operations.rb +210 -0
  38. data/lib/little_ghost/model_resolver.rb +2 -0
  39. data/lib/little_ghost/models/catalog/models_dev_source.rb +4 -1
  40. data/lib/little_ghost/provider_registry.rb +1 -0
  41. data/lib/little_ghost/providers/base.rb +16 -1
  42. data/lib/little_ghost/providers/bedrock/aws_protocol.rb +1 -1
  43. data/lib/little_ghost/providers/bedrock/http_client.rb +28 -7
  44. data/lib/little_ghost/providers/bedrock.rb +93 -8
  45. data/lib/little_ghost/providers/lm_studio/catalog_source.rb +201 -0
  46. data/lib/little_ghost/providers/lm_studio.rb +52 -0
  47. data/lib/little_ghost/providers/openai.rb +9 -5
  48. data/lib/little_ghost/providers/openai_compatible.rb +78 -1
  49. data/lib/little_ghost/runtime/hooks/artifacts.rb +8 -0
  50. data/lib/little_ghost/runtime.rb +23 -0
  51. data/lib/little_ghost/sandbox/environment_policy.rb +29 -4
  52. data/lib/little_ghost/sandbox/policy.rb +28 -6
  53. data/lib/little_ghost/sandboxes/unrestricted.rb +7 -3
  54. data/lib/little_ghost/skills/catalog.rb +13 -3
  55. data/lib/little_ghost/support/http_client.rb +10 -3
  56. data/lib/little_ghost/tool.rb +6 -1
  57. data/lib/little_ghost/tracing/open_telemetry.rb +6 -4
  58. data/lib/little_ghost/version.rb +1 -1
  59. data/lib/little_ghost.rb +26 -0
  60. metadata +45 -12
  61. data/lib/little_ghost/mcp/types.rb +0 -216
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7e4df5249569e4b93c22c44929064ad12632ca6119667429004ffc3bc4b7f7a
4
- data.tar.gz: 6ad9b9d7252c1e0749f8d4503ba820259257fb4b0e46bc638ff3bd426ed191d9
3
+ metadata.gz: d24d2ed91efec3898453d20dfea0902edfca7c207fac0983426153208b308a1e
4
+ data.tar.gz: 74aefcfaf1d9113640f80c6a7c4462c1e4919265705f0eb37c82c9f694eabcae
5
5
  SHA512:
6
- metadata.gz: 0a1e7fdc5cfe228456703b0d8b383f67461f5a465f962f010cacbee5ef55a90881f7e7135c5107745f1442a0803ddbe865b4d50912485e738b21566282404c34
7
- data.tar.gz: db5f637104b9c42a8d470b80079b29a877fddf8a41c1a5c110d8a4b926c136b36933ffe234870a0ab1a53f98083d5f856f2fd1c44ea967ae9852f5d7abebc878
6
+ metadata.gz: de9e90761042a5cad938664be479e6d35d6b0423b213cde3ac05f0a033c4de7468ef96252073b1405a73510c53b650ea5d7c74be2cae9bd68922494a05cfb406
7
+ data.tar.gz: 96ac0a88302659cad84e80c4491c23ab4bad211b0db8fe9a1bb370dbbcb0a2090d81cc3c922cc62d18ace4804a559efba5bbcb6ee44a6d52cec58ad5f45dfcc0
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Build AI features that feel at home in Ruby
2
2
 
3
3
  > **Using a coding agent?** Start with
4
- > [`llms.txt`](https://mattyr.github.io/little_ghost/llms.txt) for a concise map
5
- > of the guides and API. [`llms-full.txt`](https://mattyr.github.io/little_ghost/llms-full.txt)
4
+ > [`llms.txt`](https://littleghostai.org/llms.txt) for a concise map
5
+ > of the guides and API. [`llms-full.txt`](https://littleghostai.org/llms-full.txt)
6
6
  > contains the complete documentation in one file.
7
7
 
8
8
  LittleGhost is a Ruby library for building AI features with agents and composable assemblies. With `OPENROUTER_API_KEY` set, start with one class, give it a prompt, and call it like the rest of your application code:
@@ -20,7 +20,9 @@ run.response
20
20
  # One possible response: Hi! How can I help today?
21
21
  ```
22
22
 
23
- That small definition is already a complete agent. LittleGhost makes the model call, tracks usage, supports streaming, and closes the resources it creates for the request. Add a tool when the agent needs something from your application. Bring in more agents when the work grows.
23
+ That definition is a complete Agent. LittleGhost makes the model call, tracks
24
+ usage, supports streaming, and closes request resources. Add a Tool for
25
+ application capabilities or an Assembly as the work grows.
24
26
 
25
27
  Model requests may send system instructions, caller input, conversation history,
26
28
  Tool results, and attachments to the selected provider. Model wording can vary
@@ -40,9 +42,33 @@ $ bundle install
40
42
  $ export OPENROUTER_API_KEY="..."
41
43
  ```
42
44
 
43
- OpenRouter keeps the first setup to one credential. It is not required: LittleGhost also includes adapters for OpenAI-compatible APIs, Anthropic, Gemini, Vertex AI, and Bedrock. [Running in Production](docs/guides/production.md) shows how to configure providers and give model choices application-facing names.
45
+ The introductory guides use OpenRouter so you can start with one key. Prefer
46
+ another hosted provider or a local Ollama or LM Studio server? See [Provider
47
+ Support](docs/guides/providers.md).
44
48
 
45
- LittleGhost runs inside your Ruby process. Use it from a controller, job, CLI, or service. If you want a conventional layout, start with `app/agents`, `app/assemblies`, `app/prompts`, and `app/tools`.
49
+ LittleGhost runs inside your Ruby process. Use it from a controller, job, CLI,
50
+ or service.
51
+
52
+ ## Generate a small application
53
+
54
+ Generate a conventional standalone application:
55
+
56
+ ```sh
57
+ $ gem install little_ghost
58
+ $ little_ghost new MyApp
59
+ $ cd my_app
60
+ $ export OPENROUTER_API_KEY="..."
61
+ $ bin/little_ghost console
62
+ ```
63
+
64
+ The generator installs the bundle. `bin/little_ghost console` runs that
65
+ bundle's LittleGhost version and loads the application before starting IRB.
66
+
67
+ The source checkout also includes a complete
68
+ [single-file Agent](https://github.com/littleghostai/little_ghost/tree/main/examples/basic_agent)
69
+ and
70
+ [coding harness](https://github.com/littleghostai/little_ghost/tree/main/examples/coding_harness),
71
+ both configured for local Ollama.
46
72
 
47
73
  ## Give an agent real capabilities
48
74
 
@@ -120,21 +146,23 @@ upgrading, because interfaces may change between releases.
120
146
 
121
147
  - [Getting Started](docs/guides/getting_started.md) takes you from installation to a tool-backed, streaming agent.
122
148
  - [Core Concepts](docs/guides/core_concepts.md) builds the mental model from Agent to Assembly.
123
- - [Models and Providers](docs/guides/models_and_providers.md) gives shared model choices application-facing names.
149
+ - [Models and Providers](docs/guides/models_and_providers.md) explains targets, shared roles, and per-request model selection.
150
+ - [Provider Support](docs/guides/providers.md) has copyable setup for hosted APIs and local model servers.
124
151
  - [Prompts as Views](docs/guides/prompt_views.md) gives growing instructions, shared pieces, and application values a natural home.
125
152
  - [Tools](docs/guides/tools.md) explains how models call focused Ruby operations.
153
+ - [MCP](docs/guides/mcp.md) connects agents to operations published through the Model Context Protocol.
126
154
  - [Structured Results and Content](docs/guides/structured_outputs_and_content.md) covers checked result shapes, images, and documents.
127
155
  - [Compose Agents](docs/guides/assemblies.md) walks through workflows, swarms, graphs, nesting, and builders.
128
156
  - [Skills](docs/guides/skills.md) organizes reusable instructions and supporting resources.
129
157
  - [Workspaces and Sandboxes](docs/guides/sandboxing.md) gives files and child processes a deliberate place to run.
130
158
  - [Code Mode](docs/guides/code_mode.md) lets a model compose Tools in sandboxed Ruby or optional JavaScript.
131
- - [Integrations](docs/guides/integrations.md) connects MCP, AG-UI, and OpenTelemetry.
159
+ - [Integrations](docs/guides/integrations.md) connects Run streams to AG-UI and OpenTelemetry.
132
160
  - [Running in Production](docs/guides/production.md) covers configuration, saved conversations, supervision, and observability.
133
161
  - [API reference](rdoc-ref:LittleGhost) provides exact method signatures and ownership rules.
134
162
 
135
163
  ### For contributors
136
164
 
137
- See the [contributing guide](https://github.com/mattyr/little_ghost/blob/main/CONTRIBUTING.md), [Code of Conduct](https://github.com/mattyr/little_ghost/blob/main/CODE_OF_CONDUCT.md), and [security policy](https://github.com/mattyr/little_ghost/blob/main/SECURITY.md).
165
+ See the [contributing guide](https://github.com/littleghostai/little_ghost/blob/main/CONTRIBUTING.md), [Code of Conduct](https://github.com/littleghostai/little_ghost/blob/main/CODE_OF_CONDUCT.md), and [security policy](https://github.com/littleghostai/little_ghost/blob/main/SECURITY.md).
138
166
 
139
167
  ```sh
140
168
  $ bundle install
@@ -99,6 +99,8 @@ do not carry into a later `exec`. Within one program, the model can use:
99
99
  order.
100
100
  - `ALL_TOOLS` to inspect the complete runtime catalog.
101
101
  - `text(value)` to add user-visible output.
102
+ - Ordinary Ruby output from `puts`, `print`, `printf`, and `p`, which is
103
+ captured as user-visible output and combined into bounded chunks.
102
104
  - The program's final expression as the completed value returned by `exec` or
103
105
  a later `wait`.
104
106
  - `finish(value)` to complete early.
@@ -272,5 +274,5 @@ engine, but the generated program then has the same host access as the parent.
272
274
 
273
275
  See `LittleGhost::CodeMode::Engine`, `LittleGhost::CodeMode::Session`, and
274
276
  `LittleGhost::CodeMode::ProgramResult` for the extension contract. Continue
275
- with [Integrations](integrations.md) to connect Runs to MCP tools, AG-UI, and
276
- OpenTelemetry.
277
+ with [MCP](mcp.md) to add operations from a remote server, or
278
+ [Integrations](integrations.md) to connect Runs to AG-UI and OpenTelemetry.
@@ -17,7 +17,8 @@ $ export OPENROUTER_API_KEY="..."
17
17
 
18
18
  Use your application's secret manager outside a local shell, and never commit provider credentials.
19
19
 
20
- This guide uses OpenRouter because one credential is enough to begin. LittleGhost can use other provider connections too; you will configure those in [Running in Production](production.md).
20
+ This guide uses OpenRouter to keep setup to one key. Prefer another hosted
21
+ provider or a local model server? Start with [Provider Support](providers.md).
21
22
 
22
23
  ## See your first answer
23
24
 
@@ -201,6 +202,30 @@ app/
201
202
  └── help_center_lookup_tool.rb
202
203
  ```
203
204
 
205
+ To generate this layout for a standalone application, install the gem and run
206
+ `little_ghost new`:
207
+
208
+ ```sh
209
+ $ gem install little_ghost
210
+ $ little_ghost new MyApp
211
+ $ cd my_app
212
+ $ export OPENROUTER_API_KEY="..."
213
+ $ bin/little_ghost console
214
+ ```
215
+
216
+ The generator creates `my_app` with one Agent, a prompt view, configuration,
217
+ the conventional application directories, an installed bundle, and a local
218
+ LittleGhost command. `bin/little_ghost console` uses the generated
219
+ application's bundle, loads its configuration and classes, and then starts
220
+ IRB.
221
+
222
+ The source repository also contains a complete
223
+ [single-file Agent](https://github.com/littleghostai/little_ghost/tree/main/examples/basic_agent)
224
+ and a
225
+ [coding harness](https://github.com/littleghostai/little_ghost/tree/main/examples/coding_harness)
226
+ that demonstrates Agents, prompt views, Tools, a Graph, a Workspace, and a
227
+ native Sandbox.
228
+
204
229
  You now have the smallest useful LittleGhost application: one Agent, one Tool, and one familiar Ruby call.
205
230
 
206
231
  When the feature grows, the calling style stays the same. An **assembly** lets one or more agents work as a unit while keeping `.ask` and `.stream_ask`. Read [Core Concepts](core_concepts.md) next and grow this Agent into a larger system.
@@ -1,149 +1,8 @@
1
- # Connect MCP, AG-UI, and OpenTelemetry
1
+ # Connect Runs to interfaces and tracing
2
2
 
3
- LittleGhost can load Tools from an MCP server, translate a Run stream for an
4
- interactive interface, and publish traces. Each integration uses the same
5
- Agents and Runs you already have.
6
-
7
- ## Load Tools from an MCP server
8
-
9
- An MCP Toolset connects to one server and turns its published operations into
10
- LittleGhost Tool classes. Add the Toolset through the same Agent `tools`
11
- declaration used for local Tools:
12
-
13
- ```ruby
14
- require "little_ghost/mcp"
15
-
16
- class HelpCenterTools < LittleGhost::MCP::Toolset
17
- connection url: "https://mcp.example/rpc", timeout: 20
18
- end
19
-
20
- class CustomerSupportAgent < LittleGhost::Agent
21
- system_prompt "Use help-center tools for published guidance."
22
- tools HelpCenterTools
23
- end
24
-
25
- run = CustomerSupportAgent.ask("How long do refunds take?")
26
- run.response
27
- ```
28
-
29
- `connection` requires `url` and also accepts `headers`, `timeout`, `signer`,
30
- `allow_insecure_http`, and `max_response_bytes`. Pass a block when credentials
31
- depend on the current Agent run:
32
-
33
- ```ruby
34
- connection do |binding|
35
- token = McpAccessTokens.for_actor(binding.run.invocation.actor_id)
36
- {
37
- url: "https://mcp.example/rpc",
38
- headers: {"Authorization" => "Bearer #{token}"},
39
- timeout: 20
40
- }
41
- end
42
- ```
43
-
44
- The block's `binding` gives it access to the current Run. LittleGhost evaluates
45
- the block before opening the MCP session, so each Agent run can use credentials
46
- for its authenticated caller.
47
-
48
- By default, the Agent receives every operation published by the server. Their
49
- normalized server names, such as `search` and `fetch`, become Tool names.
50
-
51
- Use `map_tool` when the Agent should receive only part of the server catalog or
52
- when a generated Tool needs a different name or configuration:
53
-
54
- ```ruby
55
- class CuratedHelpCenterTools < LittleGhost::MCP::Toolset
56
- connection url: "https://mcp.example/rpc", timeout: 20
57
-
58
- map_tool do |tool_class, definition:, binding:|
59
- next unless %w[search fetch].include?(definition.source_name)
60
-
61
- tool_class.tool_name "help_center_#{definition.source_name}"
62
- tool_class
63
- end
64
- end
65
- ```
66
-
67
- `definition` describes the operation published by the server, and `binding`
68
- identifies the current Agent run. Return the class after configuring it, or
69
- return `nil` to omit the operation. Renaming a generated Tool does not change
70
- the original `Definition#source_name` sent back to the server.
71
-
72
- The Agent can call the generated Tools like local Tools. LittleGhost uses one
73
- local client and transport for the Toolset during the Agent run. The built-in
74
- HTTP transport does not send an MCP session-termination request. Configure
75
- server-side expiry, or arrange explicit remote cleanup when the server requires
76
- it.
77
-
78
- Most MCP results need no mapping. LittleGhost returns `structuredContent` as a
79
- Ruby Hash when present, otherwise it returns the server's text. Server images
80
- become Artifacts.
81
-
82
- Use `map_result` when one operation needs application-specific conversion. This
83
- example turns the server's download identifier into a deferred Artifact:
84
-
85
- ```ruby
86
- map_result do |result, call:, binding:|
87
- next result unless call.definition.source_name == "export"
88
-
89
- LittleGhost::Tool::Result.new(
90
- value: result.structured_content,
91
- artifacts: [
92
- LittleGhost::Artifact.deferred(
93
- reference: result.metadata.fetch("download_id"),
94
- media_type: "application/octet-stream"
95
- )
96
- ]
97
- )
98
- end
99
- ```
100
-
101
- `map_result` receives the complete `MCP::Result`, the `MCP::Call` that produced
102
- it, and the current binding. Return any Ruby value or `Tool::Result`. Returning
103
- the supplied result unchanged keeps the default conversion described above.
104
- MCP images and local Tool artifacts use the same storage and presentation
105
- rules when `Configuration#artifacts` is enabled. Images and documents are sent
106
- as model content; their stored references are fallback information rather than
107
- a second representation. LittleGhost also checks results against
108
- server-advertised JSON Schema Draft 2020-12 output schemas.
109
-
110
- An optional server can fail discovery without preventing Agent construction:
111
-
112
- ```ruby
113
- class HelpCenterTools < LittleGhost::MCP::Toolset
114
- connection { |binding| McpConnections.help_center(binding) }
115
- optional true
116
- on_error do |error, binding:|
117
- McpAvailability.report(error, run_id: binding.run.invocation.run_id)
118
- end
119
- end
120
- ```
121
-
122
- `optional true` converts expected provider and protocol discovery failures
123
- into an empty Tool set. `on_error` observes only those caught failures.
124
- Cancellation, deadlines, configuration errors, and application callback
125
- failures still propagate.
126
-
127
- LittleGhost limits the number and total size of discovered operations, the
128
- complexity of their schemas, and the size and number of returned images.
129
- `HTTPTransport` also limits each HTTP response and requires HTTPS unless local
130
- HTTP is explicitly enabled.
131
-
132
- > **Safety note:** An MCP server supplies descriptions and results that the model
133
- > can see. Structural validation does not make that content trustworthy or
134
- > authorize an operation it suggests. Expose only the operations the Agent
135
- > needs, use narrowly scoped credentials, and have the server authorize every
136
- > sensitive call. If a result becomes a deferred Artifact, its resolver must
137
- > verify that the referenced file belongs to the authenticated caller, fetch
138
- > only from an intended service, and limit the response size before returning
139
- > bytes to LittleGhost.
140
-
141
- LittleGhost implements its documented client behavior for the [MCP 2025-06-18
142
- specification](https://modelcontextprotocol.io/specification/2025-06-18).
143
-
144
- Use `LittleGhost::MCP::HTTPTransport` and `LittleGhost::MCP::Client` directly
145
- when you need a custom transport. They produce the same generated Tool classes
146
- and accept the same mapping callbacks as Toolset.
3
+ Use AG-UI to stream Run events to an interactive client. Use OpenTelemetry to
4
+ publish traces to the backend your application already uses. Neither changes
5
+ the Agent that produced the Run.
147
6
 
148
7
  ## Send a Run stream through AG-UI
149
8
 
@@ -168,13 +27,13 @@ events = LittleGhost::AGUI::Adapter.new.stream(
168
27
  events.each { |event| websocket.write(JSON.generate(event)) }
169
28
  ```
170
29
 
171
- The adapter translates text, reasoning, Tool activity, usage, retries, trace
172
- context, subagent activity, and terminal outcomes. It is stateless between
173
- calls. Your application still owns the connection, backpressure, disconnect
174
- behavior, and any request state its callbacks need.
30
+ The adapter translates the full Run, including model output, Tool activity,
31
+ retries, subagent activity, and the final outcome. It does not keep state between
32
+ calls. Your application owns the connection, backpressure, disconnect behavior,
33
+ and any request state its callbacks need.
175
34
 
176
- LittleGhost also emits namespaced custom events. Consumers should preserve or
177
- deliberately ignore event types they don't recognize. See the [AG-UI event
35
+ LittleGhost may emit event types beyond the core AG-UI set. Decide whether the
36
+ client preserves or ignores types it does not recognize. See the [AG-UI event
178
37
  documentation](https://docs.ag-ui.com/concepts/events) when implementing the
179
38
  client.
180
39
 
@@ -183,8 +42,8 @@ client.
183
42
  > see the complete Run, then filter fields before sending or storing events.
184
43
 
185
44
  Calling `each` drives the source stream on the caller's fiber or thread. When a
186
- client disconnects, stop enumerating and apply the cancellation behavior your
187
- application needs. Closing the socket can't undo Tool work that already ran.
45
+ client disconnects, stop enumerating and decide whether the application should
46
+ cancel the Run. Closing the socket cannot undo Tool work that already ran.
188
47
 
189
48
  ## Trace Runs with OpenTelemetry
190
49
 
@@ -197,9 +56,9 @@ LittleGhost.configure do |config|
197
56
  end
198
57
  ```
199
58
 
200
- LittleGhost depends on `opentelemetry-api`, leaving the SDK, processor, and
201
- exporter up to the application. It emits spans and events for Runs, Agents,
202
- model calls, Tools, assemblies, sessions, usage, and failures. Active operations
59
+ LittleGhost includes the `opentelemetry-api` integration. Your application
60
+ chooses the SDK, processor, and exporter. The subscriber emits spans and events
61
+ for Runs, model calls, Tools, assemblies, sessions, usage, and failures, and it
203
62
  can propagate W3C `traceparent` and `tracestate` fields.
204
63
 
205
64
  Prompts, messages, responses, Tool arguments, and exception content are omitted
@@ -209,9 +68,9 @@ Avoid putting raw user, order, session, or request IDs in span attributes.
209
68
 
210
69
  Attribute names follow the evolving [OpenTelemetry GenAI semantic
211
70
  conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/) where they
212
- apply. Flush or shut down `LittleGhost::Instrumentation` during application
213
- shutdown when your backend buffers data.
71
+ apply. If the tracing backend buffers data, flush or shut down
72
+ `LittleGhost::Instrumentation` before the application exits.
214
73
 
215
- See [Running in Production](production.md) for startup, shutdown, and observability,
216
- [Tools](tools.md) for local and remote Tool behavior, and [Workspaces and
217
- Sandboxes](sandboxing.md) for child processes and files.
74
+ See [Running in Production](production.md) for startup, shutdown, and
75
+ observability, [MCP](mcp.md) for operations published by remote servers,
76
+ and [Workspaces and Sandboxes](sandboxing.md) for child processes and files.
@@ -0,0 +1,288 @@
1
+ # Connect agents to MCP servers
2
+
3
+ Give an Agent access to operations published by a Model Context Protocol (MCP)
4
+ server without changing how your application calls the Agent.
5
+ `LittleGhost::MCP::Toolset` turns operations discovered by the official MCP Ruby
6
+ client into LittleGhost Tool classes, which join the same `tools` declaration as
7
+ local application Tools.
8
+
9
+ MCP support is opt-in. Requiring `little_ghost` alone does not install or load
10
+ the SDK or its transports.
11
+
12
+ ## Connect over Streamable HTTP
13
+
14
+ Add the official SDK and its HTTP dependencies to your bundle:
15
+
16
+ ```ruby
17
+ gem "mcp", "~> 1.3"
18
+ gem "faraday", "~> 2.0"
19
+ gem "event_stream_parser", "~> 1.0"
20
+ ```
21
+
22
+ Require the integration explicitly, then create a Toolset with a `client`
23
+ block. Return a new official client from the block:
24
+
25
+ ```ruby
26
+ require "little_ghost/mcp"
27
+
28
+ class HelpCenterTools < LittleGhost::MCP::Toolset
29
+ client do |_binding|
30
+ transport = MCP::Client::HTTP.new(
31
+ url: "https://mcp.example/rpc"
32
+ )
33
+ MCP::Client.new(transport:)
34
+ end
35
+ end
36
+
37
+ class CustomerSupportAgent < LittleGhost::Agent
38
+ system_prompt "Use help-center tools for published guidance."
39
+ tools HelpCenterTools
40
+ end
41
+
42
+ run = CustomerSupportAgent.ask("How long do refunds take?")
43
+ run.response
44
+ ```
45
+
46
+ LittleGhost calls the block once for each Agent run. It connects the client,
47
+ discovers the server's Tools, and closes the transport when the run ends.
48
+
49
+ The `client` block receives the current `Tool::Binding`. Use it to build headers
50
+ or credentials from authenticated application context without storing them on
51
+ the Toolset:
52
+
53
+ ```ruby
54
+ class AccountTools < LittleGhost::MCP::Toolset
55
+ client do |binding|
56
+ token = McpAccessTokens.for_actor(binding.run.invocation.actor_id)
57
+ transport = MCP::Client::HTTP.new(
58
+ url: "https://mcp.example/rpc",
59
+ headers: {"Authorization" => "Bearer #{token}"}
60
+ )
61
+ MCP::Client.new(transport:)
62
+ end
63
+ end
64
+ ```
65
+
66
+ ## Connect a local server over standard input and output
67
+
68
+ A standard input and output (stdio) connection needs only the official SDK:
69
+
70
+ ```ruby
71
+ gem "mcp", "~> 1.3"
72
+ ```
73
+
74
+ Construct the SDK transport in the same `client` block:
75
+
76
+ ```ruby
77
+ class LocalDatabaseTools < LittleGhost::MCP::Toolset
78
+ client do |_binding|
79
+ transport = MCP::Client::Stdio.new(
80
+ command: "bundle",
81
+ args: ["exec", "database-mcp"],
82
+ env: {"APP_ENV" => "production"},
83
+ read_timeout: 30
84
+ )
85
+ MCP::Client.new(transport:)
86
+ end
87
+ end
88
+ ```
89
+
90
+ A stdio server is a child process with the same operating-system permissions as
91
+ your Ruby process. It is not a sandbox. Use an operating-system sandbox when
92
+ you do not fully trust the executable.
93
+
94
+ The `env` Hash changes only the variables you name. The child inherits every
95
+ other variable from the parent process. Clear sensitive variables explicitly,
96
+ or launch the server through an isolated wrapper when it must not receive
97
+ ambient credentials.
98
+
99
+ ## Configure the official client
100
+
101
+ Set timeouts, message limits, OAuth, middleware, and other transport behavior
102
+ through the SDK. Keyword arguments on `client` go directly to
103
+ `MCP::Client#connect`; use them for an explicit protocol mode, version, or client
104
+ capability.
105
+
106
+ ### Respond to server requests
107
+
108
+ A server may need more information while handling a Tool call. MCP calls this
109
+ elicitation. Advertise the capability when connecting, then register a handler
110
+ on the client:
111
+
112
+ ```ruby
113
+ class InteractiveTools < LittleGhost::MCP::Toolset
114
+ client(capabilities: {elicitation: {}}) do |binding|
115
+ transport = MCP::Client::HTTP.new(url: "https://mcp.example/rpc")
116
+ MCP::Client.new(transport:).tap do |official_client|
117
+ official_client.on_elicitation do |request|
118
+ Elicitations.answer(request, run: binding.run)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ ```
124
+
125
+ The handler can answer from the current Run or pass the request to an application
126
+ interface. Sampling similarly lets a server request a model call, while roots
127
+ tell a server which directories the application makes available. Configure
128
+ these features on the official client.
129
+
130
+ Treat every server-initiated handler request as untrusted. Authorize it against
131
+ the current run, restrict roots to intended paths, constrain model sampling and
132
+ its cost, and return only the application data that server is allowed to receive.
133
+
134
+ Return a new, unconnected client for every run. Building it should not start
135
+ remote work. LittleGhost closes transports that provide `close`, including when
136
+ connection or discovery fails. If a custom transport owns other resources, it
137
+ must release them itself.
138
+
139
+ Set transport timeouts to match the application's deadline. LittleGhost passes
140
+ Run cancellation and deadlines to Tool discovery and calls, but connection has
141
+ its own timeout behavior.
142
+
143
+ ## Plan concurrency and cancellation
144
+
145
+ Most applications only need transport timeouts. The details below matter when
146
+ the application uses a Fiber scheduler or runs independent Tools concurrently.
147
+
148
+ ### Connection
149
+
150
+ LittleGhost connects while it discovers the Agent's Tools. Run cancellation
151
+ does not interrupt connection, so the transport's connection and read timeouts
152
+ control how long it can wait. Choose a scheduler-compatible transport adapter
153
+ when other fibers must continue during that time.
154
+
155
+ ### Tool discovery and calls
156
+
157
+ Tool discovery and calls honor Run cancellation and deadlines. Cancelling an
158
+ HTTP call returns control to the Run, but the underlying request may continue
159
+ waiting until the server responds or the transport closes.
160
+
161
+ All Tools for one run share the same client. HTTP calls may overlap when
162
+ LittleGhost runs independent Tools concurrently, so choose a Faraday adapter
163
+ that supports overlapping calls. If the server or transport requires one call
164
+ at a time, mark the generated Tools exclusive:
165
+
166
+ ```ruby
167
+ map_tool do |tool_class, mcp_tool:, binding:|
168
+ tool_class.exclusive true
169
+ tool_class
170
+ end
171
+ ```
172
+
173
+ LittleGhost serializes calls through the official stdio transport. If one of
174
+ those calls is cancelled, it closes that run's session rather than risk reading
175
+ a late response as the answer to a later call. A custom or decorated stdio
176
+ transport must provide its own serialization, cancellation behavior, and
177
+ cleanup.
178
+
179
+ ### Server handlers
180
+
181
+ The server may invoke a handler while other work is active. Write handlers for
182
+ concurrent use and capture the application values they need when building the
183
+ client. Do not rely on fiber-local state inside a handler.
184
+
185
+ ## Select and map Tools
186
+
187
+ By default, the Agent receives every Tool published by the server. LittleGhost
188
+ normalizes each name for the model and keeps the server's original name for
189
+ calls. Discovery stops after 1,000 Tools or 100 pages, which bounds the work an
190
+ untrusted catalog can create in one run.
191
+
192
+ Use `map_tool` to omit operations or configure their generated classes:
193
+
194
+ ```ruby
195
+ class CuratedHelpCenterTools < HelpCenterTools
196
+ map_tool do |tool_class, mcp_tool:, binding:|
197
+ next unless %w[search fetch].include?(mcp_tool.name)
198
+
199
+ tool_class.tool_name "help_center_#{mcp_tool.name}"
200
+ tool_class
201
+ end
202
+ end
203
+ ```
204
+
205
+ `mcp_tool` is the Tool published by the official client. Return the generated
206
+ class, a subclass, or `nil` to omit it. Renaming the generated Tool changes the
207
+ name shown to the model, not the name sent back to the server.
208
+
209
+ LittleGhost sends the server's input schema to the model unchanged. It does not
210
+ validate arguments against that schema; the server must validate them before
211
+ performing an operation. LittleGhost limits schema depth and node count during
212
+ discovery. Configure the transport's message-size limit to bound the bytes
213
+ accepted from a server.
214
+
215
+ > **Safety note:** Server descriptions and results become visible to the model.
216
+ > Treat them as untrusted content. Expose only the operations the Agent needs,
217
+ > use narrowly scoped credentials, and have the server authorize every sensitive
218
+ > call.
219
+
220
+ ## Convert results
221
+
222
+ Without a mapping, LittleGhost returns `structuredContent` when the server sends
223
+ it. Otherwise, it returns the text or remaining visible content blocks. MCP
224
+ image blocks become Artifacts.
225
+
226
+ Suppose the help-center server returns `structuredContent` with an `articles`
227
+ array. Use `map_result` to present each article as a short line:
228
+
229
+ ```ruby
230
+ map_result do |value, mcp_tool:, **|
231
+ next value unless mcp_tool.name == "search"
232
+
233
+ value.fetch("articles").map do |article|
234
+ "#{article.fetch("title")}: #{article.fetch("url")}"
235
+ end
236
+ end
237
+ ```
238
+
239
+ `value` is LittleGhost's default conversion, and `mcp_tool` identifies the
240
+ server operation. Return `value` unchanged or replace it with any Ruby value or
241
+ `Tool::Result`. The block can also receive the raw result, sent arguments, and
242
+ current binding; see the [`MCP::Toolset`
243
+ API](rdoc-ref:LittleGhost::MCP::Toolset) for their exact shapes.
244
+
245
+ Image Artifacts remain attached to the mapped result. A server result marked
246
+ `isError` remains a model-visible Tool error.
247
+
248
+ For custom images, files, or deferred Artifacts, continue with [Structured
249
+ Results and Content](structured_outputs_and_content.md). A deferred resolver
250
+ must verify ownership, fetch only from the intended service, and limit response
251
+ size.
252
+
253
+ ## Keep an optional server from blocking a run
254
+
255
+ An optional server can fail discovery without preventing Agent construction:
256
+
257
+ ```ruby
258
+ class OptionalHelpCenterTools < HelpCenterTools
259
+ optional true
260
+ on_error do |error, binding:|
261
+ McpAvailability.report(error, run_id: binding.run.invocation.run_id)
262
+ end
263
+ end
264
+ ```
265
+
266
+ `optional true` turns an expected connection or protocol error during discovery
267
+ into an empty Tool set. Configuration errors, cancellation, deadlines, and
268
+ failures in application callbacks still stop the run.
269
+
270
+ ## Let the SDK negotiate the protocol version
271
+
272
+ The [official MCP Ruby SDK](https://github.com/modelcontextprotocol/ruby-sdk)
273
+ handles protocol negotiation and transport behavior. Without explicit connect
274
+ options, it negotiates with the server using a protocol version the installed
275
+ SDK supports. Pin `protocol_version` or `mode` only when interoperability
276
+ requires it.
277
+
278
+ Need sampling, roots, elicitation, or another client capability? Configure it
279
+ on the client you build above. The installed SDK and negotiated protocol version
280
+ determine what is available. Check the [Ruby SDK client
281
+ documentation](https://ruby.sdk.modelcontextprotocol.io/client/) and the
282
+ [versioned MCP specification](https://modelcontextprotocol.io/specification/)
283
+ for the capability you need.
284
+
285
+ Continue with [Tools](tools.md) for the local Tool boundary,
286
+ [Integrations](integrations.md) to send Runs through AG-UI or OpenTelemetry, or
287
+ the [`MCP::Toolset` API](rdoc-ref:LittleGhost::MCP::Toolset) for exact mapping
288
+ and ownership contracts.