Module: Portage::Ucp::Client

Defined in:
lib/portage/ucp/client.rb,
lib/portage/ucp/client/errors.rb,
lib/portage/ucp/client/session.rb,
lib/portage/ucp/client/version.rb,
lib/portage/ucp/client/tool_result.rb,
lib/portage/ucp/client/transports/http.rb,
lib/portage/ucp/client/transports/stdio.rb,
lib/portage/ucp/client/transports/loopback.rb

Overview

Client-side counterpart to the rest of portage-ucp: everything else in this repo lets a Ruby program expose a commerce backend to agents (server side). This gem lets a Ruby program act as the shopper's agent — discover somebody else's manifest, or drive an owner's own Adapter directly, and place an order.

Defined Under Namespace

Modules: ToolResult, Transports Classes: DiscoveryError, Error, ServerError, Session

Constant Summary collapse

MANIFEST_PATH =
"/.well-known/ucp".freeze
VERSION =
"0.1.0".freeze

Class Method Summary collapse

Class Method Details

.connect(command: nil, args: [], env: nil, url: nil, headers: {}, capabilities: nil) ⇒ Object

Connects over stdio (a subprocess) or Streamable HTTP (a URL) — pass exactly one of command: or url:.



37
38
39
40
41
42
43
44
45
46
# File 'lib/portage/ucp/client.rb', line 37

def self.connect(command: nil, args: [], env: nil, url: nil, headers: {}, capabilities: nil)
  transport = if command
                Transports::Stdio.new(command: command, args: args, env: env)
              elsif url
                Transports::Http.new(url: url, headers: headers)
              else
                raise ArgumentError, "connect requires either command: or url:"
              end
  Session.new(transport: transport, capabilities: capabilities)
end

.discover(url) ⇒ Session

GETs <url>/.well-known/ucp, parses the manifest, and connects to the mcp-transport endpoint it advertises in services (see Portage::Ucp::Manifest#services — the core-gem fix this client depends on to know where to connect).

Returns:

  • (Session)

    scoped to the manifest's advertised capabilities.



53
54
55
56
# File 'lib/portage/ucp/client.rb', line 53

def self.discover(url)
  manifest = fetch_manifest(url)
  connect(url: mcp_endpoint(manifest), capabilities: capability_names(manifest))
end

.for_adapter(adapter, **server_opts) ⇒ Object

Wraps an already-built Adapter directly — no subprocess/socket. Still runs the real merchant-side contract (authenticator, rate limiter, Dispatcher, WireEnvelope, via Portage::Ucp::Mcp::Server), just in-process. This is the transport for the "your own store" case: you already have credentials for this Adapter, so there's no manifest to discover and no wire hop to make.



31
32
33
# File 'lib/portage/ucp/client.rb', line 31

def self.for_adapter(adapter, **server_opts)
  Session.new(transport: Transports::Loopback.new(adapter: adapter, **server_opts))
end