Skip to content

feat: JSON-RPC 2.0 transport with rpc.discover (OpenRPC) - #21

Merged
dskripchenko merged 1 commit into
masterfrom
feature/jsonrpc-transport
Oct 26, 2026
Merged

dskripchenko merged 1 commit into
masterfrom
feature/jsonrpc-transport

Conversation

@dskripchenko

Copy link
Copy Markdown
Owner

Stage 2, step 3: the second non-HTTP transport, on Runtime\Dispatcher like MCP (#20).

What

With laravel-api.jsonrpc.enabled, every served version answers JSON-RPC 2.0 at POST /{prefix}/rpc/{version}:

  • Methods controller.action, params by name (an object; positional → -32602: actions have no argument order). Called through the dispatcher — router, middleware cascade, validation, as the caller of the HTTP request (headers passed on).
  • Result = the action's payload. A failed action is an error: -32602 (Invalid params) when the action answered 422 — a validation failure — and -32000 otherwise; message = the payload's message (or errorKey); data: {status, payload} with errorKey and all.
  • Batches (answered in order, notifications left out; empty batch → -32600; invalid items → -32600 with id: null), notifications (carried out, never answered; all-notification → 204). Protocol errors: -32700 parse, -32600 invalid request, -32601 method not found. HTTP status 200 whenever there is a body.
  • rpc.discover → an OpenRPC 1.3.2 document of the version: one method per action (summary = first summary line, description, paramStructure: by-name, one param per input field with required/description/schema, result.schema = the payload schema or the 200 @response template, deprecated), templates under components.schemas — same #/components/schemas/… spelling, so refs pass through unchanged.
  • 'jsonrpc' => false on an action/controller leaves it out; config laravel-api.jsonrpc (enabled / LARAVEL_API_JSONRPC, versions, path, middleware); route api-jsonrpc.

Shared with MCP now: Transports\ServedVersions (which versions a transport serves) and Transports\NamedOperations (the controller.action → Operation index from the route map, with the per-transport opt-out). McpToolset / McpController use them; behaviour unchanged.

Checked with the official validator

The OpenRPC documents of all 13 fixture APIs and the example go through @open-rpc/schema-utils-js (validateOpenRPCDocument + dereferenceDocument). The first run failed on 2 of 7: an action without @output fields had result.schema.properties: [] — PHP's empty array encodes as a JSON list where JSON Schema wants a map. New Operations\Schema\JsonSchemas::encodable() writes empty properties / patternProperties / $defs / definitions as {}; used by the OpenRPC document and by MCP's inputSchema ($defs of a template without fields had the same problem). After it: 13/13 valid and dereferenceable.

The OpenAPI document has the same bug — "properties": [] 21 times across 6 golden-master snapshots (empty @output, envelope payloads). Not changed here: it moves the snapshots and deserves its own PR — next.

Tests

635 passing; golden-master unchanged. New tests/Feature/Transports/JsonRpcTest.php (12): off by default; call + result; by-name params + header forwarding; failed action → -32000 with data; protocol errors; batch order with notifications and invalid items; notifications-only → 204; served versions; rpc.discover shape; a real validation failure → -32602, status 422, field errors in data (own module + routes); 'jsonrpc' => false; JsonSchemas::encodable(); no "properties":[] in a discover document.

Docs: README (en/ru/zh/de) "JSON-RPC 2.0", config comments, CHANGELOG, CLAUDE.md.

🤖 Generated with Claude Code

With laravel-api.jsonrpc.enabled, POST /{prefix}/rpc/{version} serves
every action as a method controller.action with by-name params, through
Runtime\Dispatcher. Failures are errors (-32602 for 422, -32000 otherwise,
status and payload in data); batches and notifications per spec;
rpc.discover answers with an OpenRPC 1.3.2 document. ServedVersions and
NamedOperations are shared with MCP. JsonSchemas::encodable() writes empty
properties/$defs as {} — the OpenRPC validator rejected [].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dskripchenko
dskripchenko merged commit 1e05370 into master Oct 26, 2026
7 checks passed
@dskripchenko dskripchenko mentioned this pull request Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant