MCP

Riffer can consume third-party Model Context Protocol (MCP) servers as tool sources. Tools are discovered automatically at registration time — no handwritten Ruby per tool required.

Overview

  1. Register an MCP server globally with Riffer::Mcp.register.

  2. Opt an agent into that server’s tools using the use_mcp DSL.

  3. The agent picks up the tools and calls them like any other Riffer tool.

Registering a Server

Riffer::Mcp.register(
  name: "github",        # unique identifier
  tags: [:github],       # agents opt-in by tag
  endpoint: "https://mcp.github.com",
  discovery_headers: -> { {"Authorization" => "Bearer #{ENV['GITHUB_TOKEN']}"} }
)

register blocks until tool discovery completes (or fails). Discovery uses the configured Riffer::Runner (default Runner::Sequential — inline). To run discovery on a pool thread (e.g. for Rails connection-pool isolation), set config.mcp.discovery_runner = Riffer::Runner::Threaded.new; register still blocks but the work runs off the calling thread.

Discovery headers

discovery_headers is a Hash or Proc used only for MCP tools/list when the discovery client is built (the Proc runs once at that time). Use it for bootstrap identity: service account, env-based token, or {} if listing is unauthenticated.

Optional credentials_scope on the manifest documents whether you expect invocation headers to depend on tenant and/or user keys in the agent context (e.g. :global, :tenant, :user). It does not store tenant or user ids — only a hint for your app and docs. For multi-tenant apps, :user often means “user in tenant” and your context may include both tenant and user identifiers.

Session credentials callback

When Riffer.config.mcp.credentials is set to a Proc, each MCP tools/call resolves HTTP headers through that callback instead of reusing discovery_headers.

Signature:

Riffer.configure do |config|
  config.mcp.credentials = lambda do |manifest:, matched_tags:, context:|
    # return nil to omit this server's tools for this agent run (at resolve time)
    # return Hash<String,String> headers for tools/call (e.g. Authorization)
  end
end

Resolve time: Before tools are exposed to the model, the proc is invoked once per matching registration. If it returns nil, that server’s tools are omitted for this run (e.g. tenant has no integration).

Call time: Authenticated tool wrappers invoke the proc again for each execution. If it returns nil, Riffer::Mcp::CredentialsDeniedError is raised.

If credentials is unset, discovery and tools/call share one client built from discovery_headers (same behaviour as a single static token for both list and call).

Tags

Tags are entirely up to your application; Riffer does not define a canonical vocabulary. Each server may declare multiple tags; an agent includes every registration that shares any tag passed to use_mcp.

When MCPs are registered, assign stable bucket tags so agent classes can opt in without listing every server name—for example tags: [:connectors, :github] with use_mcp :connectors for all enabled connectors, or use_mcp :github for that integration only.

Registrations are global (endpoint + tags); tenant and user access are enforced in your credentials proc and whatever you put in context, not by putting ids on the manifest.

Opting an Agent In

class ResearchAgent < Riffer::Agent
  model "openai/gpt-5-mini"
  instructions "You are a research assistant."

  use_mcp :github
end

use_mcp accepts any tag registered via Riffer::Mcp.register. Multiple calls accumulate — the agent receives tools from all matching servers:

class MultiAgent < Riffer::Agent
  model "openai/gpt-5-mini"

  use_mcp :github
  use_mcp :jira
end

MCP tools are appended after any tools declared with uses_tools.

Tool names must be unique across uses_tools and all included MCP servers; duplicate names raise Riffer::ArgumentError when tools are resolved.

Subclassing

Like uses_tools, use_mcp is not inherited from the superclass. Declare use_mcp on each agent class that should load MCP tools.

Unregistering a Server

Riffer::Mcp.unregister("github")

Subsequent agent runs will not include tools from that server. Call register again (with fresh discovery_headers if needed) to re-register.

Introspection

Riffer::Mcp.registrations
# => {"github" => #<Riffer::Mcp::Registration ...>, ...}

reg = Riffer::Mcp.registrations["github"]
reg.tools    # => [<Class:...>, ...]  (Riffer::Tool subclasses)

Discovery failures raise from register directly, typically Faraday::Error for network issues or Riffer::DependencyError if the mcp/faraday gems are missing. Rescue StandardError for graceful degradation:

begin
  Riffer::Mcp.register(name: "github", ...)
rescue StandardError => e
  Rails.logger.warn("MCP registration failed: #{e.message}")
end

Error Classes

Class Raised when
Riffer::Mcp::CredentialsDeniedError credentials proc returns nil during tools/call

All inherit from Riffer::Mcp::Error < Riffer::Error.

Limitations

Requirements

The mcp and faraday gems are optional dependencies — they are not included in Riffer’s runtime gemspec. To use MCP integration, add both to your own Gemfile:

gem "mcp"
gem "faraday"

Faraday is required for the MCP HTTP transport. If either gem is missing when Riffer::Mcp.register is called, a Riffer::DependencyError is raised.