Project

clamo

0.0
A long-lived project that still receives updates
Minimal, spec-compliant JSON-RPC 2.0 server for Ruby. Expose any module or class as a JSON-RPC service with request validation, batch processing, and notification support.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

Clamo

CI Gem Version Ruby License: MIT JSON-RPC 2.0

A Ruby implementation of JSON-RPC 2.0 designed for simplicity and compliance with the specification. Expose any Ruby module or class as a JSON-RPC service with minimal effort — just point Clamo at your object and its public methods become callable.

JSON-RPC 2.0 is the transport protocol behind MCP and LSP — Clamo makes it easy to build spec-compliant services in Ruby.

Table of Contents

  • Installation
  • Usage
    • Basic Usage
    • Batch Requests
    • Notifications
  • Error Handling
  • Configuration
    • Error Callback
    • Per-Call Configuration
  • Advanced Features
    • Parallel Processing
    • Building JSON-RPC Requests
  • Roadmap
  • Development
  • Contributing
  • License

Installation

Add to your Gemfile:

gem "clamo"

Or install directly:

gem install clamo

Usage

Basic Usage

require 'clamo'

# Define a service object with methods you want to expose
module MyService
  def self.add(a, b)
    a + b
  end

  def self.subtract(a:, b:)
    a - b
  end

  # Private methods won't be accessible via JSON-RPC
  private_class_method def self.internal_method
    # This won't be exposed
  end
end

# JSON string in, JSON string out — the primary entry point for HTTP/socket integrations.
# Returns nil for notifications (no response expected).
json_response = Clamo::Server.handle_json(
  request: '{"jsonrpc": "2.0", "method": "add", "params": [1, 2], "id": 1}',
  object: MyService
)
# => '{"jsonrpc":"2.0","result":3,"id":1}'

If you need the parsed hash instead of a JSON string, use the lower-level methods directly or via their shorter aliases:

# From a JSON string
response = Clamo::Server.dispatch_json(
  request: '{"jsonrpc": "2.0", "method": "add", "params": [1, 2], "id": 1}',
  object: MyService
)
# => {"jsonrpc" => "2.0", "result" => 3, "id" => 1}

# From a pre-parsed hash
response = Clamo::Server.dispatch(
  request: { "jsonrpc" => "2.0", "method" => "add", "params" => [1, 2], "id" => 1 },
  object: MyService
)

The longer names parsed_dispatch_to_object, unparsed_dispatch_to_object, and handle still work as deprecated aliases.

Both positional ([1, 2]) and named ({"a": 5, "b": 3}) parameters are supported — they map to positional and keyword arguments on the Ruby method respectively.

Batch Requests

Clamo handles batch requests automatically:

batch_request = <<~JSON
[
  {"jsonrpc": "2.0", "method": "add", "params": [1, 2], "id": 1},
  {"jsonrpc": "2.0", "method": "subtract", "params": {"a": 5, "b": 3}, "id": 2}
]
JSON

batch_response = Clamo::Server.dispatch_json(
  request: batch_request,
  object: MyService
)

puts batch_response
# => [{"jsonrpc" => "2.0", "result" => 3, "id" => 1}, {"jsonrpc" => "2.0", "result" => 2, "id" => 2}]

Notifications

Notifications are requests without an ID field. They don't produce a response:

notification = '{"jsonrpc": "2.0", "method": "add", "params": [1, 2]}'
response = Clamo::Server.dispatch_json(
  request: notification,
  object: MyService
)

puts response
# => nil

Error Handling

Clamo follows the JSON-RPC 2.0 specification for error handling:

Error Code Message Description
-32700 Parse error Invalid JSON was received
-32600 Invalid request The JSON sent is not a valid Request object
-32601 Method not found The method does not exist / is not available
-32602 Invalid params Invalid method parameter(s) or arity mismatch
-32603 Internal error Internal JSON-RPC error
-32000 Server error Exception raised by dispatched method

Parameter arity is validated before dispatch. If the number of positional arguments or keyword arguments doesn't match the Ruby method signature, a -32602 Invalid params error is returned.

Configuration

Error Callback

Errors during dispatch are reported through on_error, which is called for both requests and notifications. Notifications are silent by default (no response is sent to the client, but on_error still fires); requests return a generic -32000 Server error without leaking exception details. Use on_error to capture the full exception for logging:

Clamo::Server.on_error = ->(exception, method, params) {
  Rails.logger.error("#{method} failed: #{exception.message}")
}

Per-Call Configuration

Configuration can be overridden per-call. Module-level settings serve as defaults:

Clamo::Server.handle_json(
  request: body,
  object: MyService,
  on_error: ->(e, method, params) { MyLogger.error(e) }
)

Per-call config is snapshotted at the start of each dispatch, so concurrent mutations to module-level settings cannot affect in-flight requests.

Advanced Features

Parallel Processing

Batch requests are processed in parallel when the parallel gem is available. If parallel is not installed, batches fall back to sequential processing. You can pass options to Parallel.map:

Clamo::Server.dispatch(
  request: batch_request,
  object: MyService,
  in_processes: 4  # Parallel processing option
)

Building JSON-RPC Requests

Clamo provides utilities for building JSON-RPC requests:

request = Clamo::JSONRPC.build_request(
  method: "add",
  params: [1, 2],
  id: 1
)

puts request
# => {"jsonrpc" => "2.0", "method" => "add", "params" => [1, 2], "id" => 1}

Roadmap

  • Per-call configuration
  • Method metadata caching
  • Method allowlists/denylists
  • Observability and logging
  • Profiling
  • Hooks
  • Rack helpers and framework helpers (Rails)
  • Autodoc (Markdown?)
  • Error data builder
  • Schemas
  • Method auto-prefixing (namespace to prefix)
  • Multiple objects (namespace fusion)
  • Context injection
  • More examples

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/rubakas/clamo.

License

The gem is available as open source under the terms of the MIT License.