NAME
Catalyst::Plugin::JSONRPC::Server - Generic JSON-RPC 2.0 server plugin for Catalyst
SYNOPSIS
package MyApp;
use Catalyst qw/+Catalyst::Plugin::JSONRPC::Server/;
__PACKAGE__->setup;
sub rpc :Path('/rpc') :Args(0) {
my ( $self, $c ) = @_;
$c->jsonrpc_register( add => sub ($params) { $params->{a} + $params->{b} } );
$c->jsonrpc_dispatch;
}
DESCRIPTION
Adds JSON-RPC 2.0 request dispatch to a Catalyst application. The protocol engine lives in Catalyst::Plugin::JSONRPC::Server::Dispatcher; this module is the thin Catalyst seam: it adds jsonrpc_register and jsonrpc_dispatch to the context.
The dispatcher used by jsonrpc_register/jsonrpc_dispatch is built fresh for each request and cached on the context, so handlers (and anything they close over, such as $c) never leak into a later request. It is therefore safe to register handlers inside a request action, as the SYNOPSIS does.
CONFIGURATION
Under the 'Catalyst::Plugin::JSONRPC::Server' config key:
max_body_bytes-
Maximum raw request body size the plugin will read (default 10 MiB;
0= unlimited). A larger body is rejected with a-32600"Request too large".
The per-request dispatcher's max_batch (maximum batch-array length, default 1000; 0 = unlimited) is a Catalyst::Plugin::JSONRPC::Server::Dispatcher attribute; supply your own dispatcher via jsonrpc_dispatch_with to change it.
METHODS
jsonrpc_register( $method => $coderef )
Register a handler for a JSON-RPC method name. The handler is invoked as $coderef->($params), where $params is the request's params (an arrayref, hashref, or undef). Return the result; to signal a JSON-RPC error, throw a Catalyst::Plugin::JSONRPC::Server::Error (or die with a { code, message, data } hashref). A plain die becomes a -32603 internal error whose text is not leaked. Returns $c (chainable).
jsonrpc_dispatch( $body = undef )
Dispatch a JSON-RPC 2.0 request. Pass the raw JSON body, or omit it to have the plugin read the raw request body from $c->request->body. Writes the HTTP response (200 with the JSON envelope for a result or error, or 204 with an empty body when there is nothing to send, i.e. a notification) and returns the response data (hashref or arrayref) or undef.
Delegates to jsonrpc_dispatch_with using the per-request dispatcher.
jsonrpc_dispatch_with( $dispatcher, $body = undef, $empty_status = 204 )
Like jsonrpc_dispatch, but dispatches against a caller-supplied Catalyst::Plugin::JSONRPC::Server::Dispatcher. Use this when you want to control the dispatcher yourself, e.g. to pre-register a fixed handler set once, or to set a non-default max_batch. (A consumer such as an MCP plugin builds a fresh per-request dispatcher and dispatches it here.)
$dispatcher must be a Catalyst::Plugin::JSONRPC::Server::Dispatcher instance. $body is the raw JSON string; when omitted the plugin reads the raw request body from $c->request->body (subject to max_body_bytes). $empty_status is the HTTP status used when there is nothing to send (a notification or all-notification batch); it defaults to 204, but a transport that requires a different code (e.g. MCP's Streamable HTTP, which mandates 202 Accepted) can pass it. Writes the HTTP response and returns the response data (hashref or arrayref) or undef. A handler result that will not serialize degrades to a -32603 error rather than dying (see "encode_safe" in Catalyst::Plugin::JSONRPC::Server::Dispatcher).
EXAMPLES
A runnable example lives in examples/: a small Catalyst app exposing echo and sum over JSON-RPC, plus a core-Perl client. Start it with plackup examples/app.psgi and run perl examples/client.pl. See examples/README.md.
AUTHOR
Mike Whitaker <mike@altrion.org>
Built with tool assistance from Claude Code/(mostly) Opus 4.8 to accelerate code generation and maximise test coverage (and reduce typing :D).
With thanks to
Jesse Vincent for
/superpowers(https://github.com/obra/superpowers) and theAGENTS.mdboilerplateCurtis "Ovid" Poe for
/paad(https://github.com/Ovid/paad)
for providing an agentic development framework that keeps code authority firmly where it belongs.
Iteratively reviewed by Finn Kempers <finn@shadow.cat> with analysis from ZCode/GLM-5.2.
LICENSE
This library is free software; you can redistribute it and/or modify it under the terms of the Artistic License, as distributed with Perl.