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

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.