scriptling.runtime.jsonrpc

Overview

The scriptling.runtime.jsonrpc library registers handlers for a JSON-RPC 2.0 server that runs over stdin/stdout or HTTP: it does not make outbound JSON-RPC calls. Handlers are referenced by string ("library.function"), the same model used by runtime.http, so each request runs on a fresh, isolated evaluator in its own goroutine and a slow handler never blocks the next request.

Start the stdio server with the --json-rpc flag:

scriptling --json-rpc setup.py

Logging is automatically redirected to stderr so it never corrupts the JSON-RPC stream on stdout.

Start the HTTP server with --server and --json-rpc:

scriptling --server :8000 --json-rpc setup.py

HTTP JSON-RPC is served at POST /json-rpc and can run alongside normal runtime.http routes and MCP tools.

Available Functions

Function Description
method(name, handler) Register a JSON-RPC method handler
notification(name, handler) Register a notification handler (no response)
error(code, message, data=None) Build a structured JSON-RPC error response

Functions

method(name, handler)

Registers a JSON-RPC method handler. The handler receives the decoded JSON-RPC params as its single argument and returns a JSON-compatible result. Raise an exception, or return jsonrpc.error(...), to produce an error response.

Parameters:

  • name (str): JSON-RPC method name.
  • handler (str): Handler function as "library.function".

Returns: None

# setup.py
import scriptling.runtime as runtime

runtime.jsonrpc.method("echo", "handlers.echo")
runtime.jsonrpc.method("divide", "handlers.divide")
# handlers.py
import scriptling.runtime as runtime

def echo(params):
    return params

def divide(params):
    if params["b"] == 0:
        return runtime.jsonrpc.error(-32602, "division by zero", {"field": "b"})
    return params["a"] / params["b"]

notification(name, handler)

Registers a JSON-RPC notification handler. Notifications are JSON-RPC requests without an id field. The handler receives the decoded params but no response is written, and return values are ignored.

Parameters:

  • name (str): JSON-RPC notification name.
  • handler (str): Handler function as "library.function".

Returns: None

import scriptling.runtime as runtime

def on_progress(params):
    # Side effects only; no response is written.
    pass

runtime.jsonrpc.notification("progress", "handlers.on_progress")

error(code, message, data=None)

Builds a structured JSON-RPC error response. Return this from a method handler to emit a JSON-RPC error response with a custom code.

Parameters:

  • code (int): JSON-RPC error code (e.g. -32602 for invalid params).
  • message (str): Human-readable error message.
  • data (any, optional): Optional structured data attached to the error. Default: None.

Returns: JSONRPCError: instance recognized by the server and converted into a JSON-RPC error response.

import scriptling.runtime as runtime

def divide(params):
    if params["b"] == 0:
        return runtime.jsonrpc.error(-32602, "division by zero", {"field": "b"})
    return params["a"] / params["b"]

Concurrency Model

Each request is dispatched on its own goroutine with a fresh Scriptling evaluator, mirroring runtime.http, MCP, and WebSocket serving. Handlers cannot share in-memory state across requests; coordinate through runtime.kv or runtime.sync instead.

Stdio Wire Format

  • Requests are newline-delimited JSON objects (NDJSON), one per line.
  • Batches (JSON arrays) are supported: each element is dispatched concurrently and a single JSON array of responses is returned.
  • Notifications (requests with no id) produce no response.
  • Integer precision is preserved (numbers are not coerced to floats).

HTTP Wire Format

  • Send a single JSON-RPC object or batch array with POST /json-rpc.
  • Requests return 200 application/json.
  • Notifications and all-notification batches return 204 No Content.
  • Notifications inside a mixed batch are handled but omitted from the response array.
  • Integer precision is preserved.

Error Codes

Code Meaning Source
-32700 Parse error Malformed JSON on stdin or in the HTTP request body
-32600 Invalid request Not a valid JSON-RPC 2.0 request
-32601 Method not found No handler registered for method
-32602 Invalid params Params failed to decode (or via error())
-32603 Internal error Request cancelled
-32000 Server error Handler raised an exception or returned an error

Examples

Sample stdio session

$ echo '{"jsonrpc":"2.0","method":"echo","params":{"hello":"world"},"id":1}' \
  | scriptling --json-rpc setup.py
{"jsonrpc":"2.0","result":{"hello":"world"},"id":1}

$ echo '[{"jsonrpc":"2.0","method":"divide","params":{"a":1,"b":0},"id":1},
         {"jsonrpc":"2.0","method":"echo","params":42,"id":2}]' \
  | scriptling --json-rpc setup.py
[{"jsonrpc":"2.0","error":{"code":-32602,"message":"division by zero","data":{"field":"b"}},"id":1},
 {"jsonrpc":"2.0","result":42,"id":2}]

Sample HTTP session

$ curl -X POST http://127.0.0.1:8000/json-rpc \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"echo","params":{"hello":"world"},"id":1}'
{"jsonrpc":"2.0","result":{"hello":"world"},"id":1}

$ curl -i -X POST http://127.0.0.1:8000/json-rpc \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"progress","params":{"done":1}}'
HTTP/1.1 204 No Content

Notes

  • Methods/notifications are registered during setup script execution, then frozen.
  • The JSONRPCError class is exposed as runtime.jsonrpc.JSONRPCError for type checks.
  • Use --json-rpc together with --kv-storage or --libpath exactly as you would for the HTTP --server mode.

Security Considerations

This is an extended library, requiring registration in Go, see Library Registration.

scriptling.runtime.jsonrpc is server-only: it registers method/notification handlers for an incoming JSON-RPC stream and never issues outbound JSON-RPC calls itself. The risk shape matches runtime.http: registering a method exposes that handler to any peer that can reach the stdio stream or POST /json-rpc endpoint, so treat every registered method as a network-reachable entry point and validate params defensively. For a full risk breakdown across all libraries, see the Security Guide.

See Also