Resource-Oriented JSON-RPC (RO-JRPC) 1.0
A Backward-Compatible Structured Routing Extension for JSON-RPC 2.0
From: https://github.com/ggzy12345/ro-jrpc/
| Field | Value |
|---|---|
| Status | Draft |
| Category | Standards Track |
| Version | 1.0 Draft |
| Depends on | JSON-RPC 2.0 |
Table of Contents
- Abstract
- Terminology
- Compatibility
- Request Object
- Semantics
- Canonical Method Mapping
- Example Requests
- Responses
- Receiver Behavior
- Notifications
- Discovery
- Authorization Model
- Transport Independence
- Implementation Guidance
- Security Considerations
- Versioning
- Rationale
- Caching
- Async Results
- Example: AI Agent Usage
- Example: CLI Usage
1. Abstract
This specification defines Resource-Oriented JSON-RPC (RO-JRPC), a backward-compatible extension to JSON-RPC 2.0 that standardizes structured routing fields for request messages.
RO-JRPC introduces the following extension fields:
resourcetargetverbsubresourceparent
These fields make explicit the semantics commonly encoded inside JSON-RPC method strings such as:
user.create
task.cancel
file.read
RO-JRPC preserves compatibility with existing JSON-RPC 2.0 tooling while enabling improved:
- Authorization
- Capability discovery
- Analytics
- Code generation
- Agent/tool protocols
- CLI/local IPC systems
- Async result routing
2. Terminology
The key words MUST, SHOULD, MAY, MUST NOT, and SHOULD NOT are to be interpreted as described in RFC 2119.
3. Compatibility
A valid RO-JRPC request MUST also be a valid JSON-RPC 2.0 request.
Therefore:
-
jsonrpcMUST equal"2.0" -
methodMUST be present -
idfollows JSON-RPC 2.0 rules - Notifications remain supported
RO-JRPC adds optional members and semantics only.
4. Request Object
4.1 Standard Members
RO-JRPC inherits all JSON-RPC 2.0 request members:
| Member | Requirement |
|---|---|
jsonrpc |
REQUIRED |
method |
REQUIRED |
params |
OPTIONAL |
id |
OPTIONAL |
4.2 Extension Members
All extension members are OPTIONAL. resource and verb MUST appear together — one without the other is invalid. subresource and parent MUST NOT appear unless resource is also present.
| Member | Type | Meaning |
|---|---|---|
resource |
string |
Logical entity class or collection |
target |
`string \ | number` |
verb |
string |
Action to perform |
subresource |
string |
Nested entity class owned by resource
|
parent |
`string \ | number` |
meta |
object |
Optional metadata (servers SHOULD NOT trust) |
cache |
`string \ | object` |
request_id |
`string \ | number` |
5. Semantics
5.1 Resource
resource identifies the logical subject of the operation.
Examples: user, task, file, repo, session, tool, agent
5.2 Target
target identifies a specific instance of a resource.
Examples: 42, "abc123", "/tmp/demo.txt", "session-9"
5.3 Verb
verb identifies the requested action.
Recommended verbs:
| Verb | Meaning |
|---|---|
get |
Retrieve a single resource |
list |
Retrieve multiple resources |
create |
Create a new resource |
update |
Modify an existing resource |
delete |
Remove a resource |
execute |
Run a procedure or tool |
cancel |
Abort an in-progress operation |
watch |
Subscribe to resource changes |
describe |
Return capability metadata |
lock |
Acquire a lock on a resource |
unlock |
Release a lock on a resource |
Async result verbs:
| Verb | Meaning |
|---|---|
yield |
Intermediate async result; more messages expected |
return |
Final async result; no further messages expected |
yield and return are used in result messages flowing back to the originating client (see §19). They SHOULD NOT be used in client-to-server requests.
Implementations MAY define custom verbs.
5.4 Sub-Resource
subresource identifies a nested entity class that is owned by and scoped to a resource.
Examples: repo → issue, project → task, session → message, org → member
Rules:
-
subresourceMUST NOT be present unlessresourceis also present. - Nesting beyond one level (sub-sub-resources) is explicitly out of scope. If deeper nesting is required, the resource model SHOULD be flattened.
- When
subresourceis present,targetidentifies a specific instance of thesubresource, not ofresource.
5.5 Parent
parent identifies the specific instance of resource that owns the subresource being addressed.
-
parentMUST NOT be present unlesssubresourceis also present. -
parentplays the same role forresourcethattargetplays forsubresource.
6. Canonical Method Mapping
When resource and verb are present, method MUST equal:
- Without
subresource:<resource>.<verb> - With
subresource:<resource>.<subresource>.<verb>
resource |
subresource |
verb |
method |
|---|---|---|---|
user |
— | create |
user.create |
task |
— | cancel |
task.cancel |
repo |
— | clone |
repo.clone |
repo |
issue |
get |
repo.issue.get |
project |
task |
list |
project.task.list |
org |
member |
delete |
org.member.delete |
job |
— | yield |
job.yield |
job |
— | return |
job.return |
6.1 Target Exclusion Rule
target SHOULD NOT be embedded in method.
Preferred:
{
"method": "user.get",
"resource": "user",
"target": "42",
"verb": "get"
}
Avoid:
user.42.get
7. Example Requests
7.1 Create User
{
"jsonrpc": "2.0",
"method": "user.create",
"resource": "user",
"verb": "create",
"params": { "name": "Alice" },
"id": 1
}
7.2 Get User
{
"jsonrpc": "2.0",
"method": "user.get",
"resource": "user",
"target": "42",
"verb": "get",
"id": 2
}
7.3 Cancel Task
{
"jsonrpc": "2.0",
"method": "task.cancel",
"resource": "task",
"target": "123",
"verb": "cancel",
"id": "abc"
}
7.4 Get a Sub-Resource (Issue on a Repo)
{
"jsonrpc": "2.0",
"method": "repo.issue.get",
"resource": "repo",
"parent": "99",
"subresource": "issue",
"target": "7",
"verb": "get",
"id": 3
}
7.5 List Sub-Resources (Tasks in a Project)
{
"jsonrpc": "2.0",
"method": "project.task.list",
"resource": "project",
"parent": "42",
"subresource": "task",
"verb": "list",
"id": 4
}
7.6 Create a Sub-Resource (Message in a Session)
{
"jsonrpc": "2.0",
"method": "session.message.create",
"resource": "session",
"parent": "session-9",
"subresource": "message",
"verb": "create",
"params": { "content": "Hello" },
"id": 5
}
8. Responses
Responses MUST remain compliant with JSON-RPC 2.0.
Success:
{
"jsonrpc": "2.0",
"result": { "ok": true },
"id": "abc"
}
Error:
{
"jsonrpc": "2.0",
"error": {
"code": -32600,
"message": "Invalid Request: verb not supported"
},
"id": "abc"
}
9. Receiver Behavior
9.1 Structured Requests
If resource and verb are present, receivers MUST route using those members.
If subresource is also present, receivers MUST route using resource, subresource, and verb together.
9.2 Legacy Requests
If only method is present, receivers MAY route using method parsing or legacy dispatch.
9.3 Mismatch Handling
If method, resource, subresource, and verb are inconsistent — for example:
method = "user.create"
resource = "task"
verb = "delete"
Or:
method = "repo.issue.get"
resource = "repo"
subresource = "comment"
verb = "get"
The receiver MUST reject with -32600 Invalid Request.
9.4 Co-presence Rules
The following combinations are invalid and MUST be rejected with -32600:
| Condition | Invalid because |
|---|---|
resource present, verb absent |
Incomplete structured request |
verb present, resource absent |
Incomplete structured request |
subresource present, resource absent |
Sub-resource requires parent resource |
parent present, subresource absent |
parent is meaningless without sub-resource |
target present, resource absent |
Target has no resource context |
10. Notifications
Notifications are requests without id. No response is sent.
{
"jsonrpc": "2.0",
"method": "log.create",
"resource": "log",
"verb": "create",
"params": { "message": "started" }
}
11. Discovery
Servers SHOULD support rpc.describe.
Request:
{
"jsonrpc": "2.0",
"method": "rpc.describe",
"resource": "rpc",
"verb": "describe",
"id": 1
}
Response:
{
"jsonrpc": "2.0",
"result": {
"protocol": "ro-jrpc",
"version": "1.0-draft",
"resources": [
{
"name": "user",
"verbs": ["create", "get", "update", "delete"]
},
{
"name": "task",
"verbs": ["list", "cancel"]
},
{
"name": "repo",
"verbs": ["get", "list", "clone"],
"subresources": [
{ "name": "issue", "verbs": ["get", "list", "create", "delete"] }
]
}
]
},
"id": 1
}
The protocol and version fields in the result allow clients to confirm RO-JRPC support and negotiate behavior. Servers SHOULD include both.
12. Authorization Model
RO-JRPC enables structured policy systems. The full authorization tuple is:
resource [+ subresource] + verb [+ target | parent]
This is the primary advantage over method-string ACLs — policies can be written at any granularity without string parsing.
Flat resource policies:
allow user:create
allow task:get target=*
deny file:delete target=*
allow file:delete target=own
Sub-resource policies:
allow repo:issue:get parent=*
allow repo:issue:create parent=own
deny org:member:delete parent=*
Target wildcards:
-
target=*— any instance -
target=own— only instances owned by the requesting identity -
target=<id>— a specific instance
The policy syntax above is illustrative. Implementations MAY define their own policy language; what RO-JRPC standardizes is the tuple structure, not the policy format.
13. Transport Independence
RO-JRPC MAY be used over:
- stdio
- pipes
- Unix domain sockets
- TCP
- HTTP
- WebSocket
- Child-process IPC
14. Implementation Guidance
Preferred Internal Router API
// Flat resources
router.resource("user").verb("create", handler)
router.resource("task").verb("cancel", handler)
// Sub-resources
router.resource("repo").subresource("issue").verb("get", handler)
router.resource("project").subresource("task").verb("list", handler)
// Async result verbs
router.resource("job").verb("yield", handler)
router.resource("job").verb("return", handler)
Compatibility Adapter
If structured members are absent, receivers MAY parse method:
user.create → resource=user verb=create
repo.issue.get → resource=repo subresource=issue verb=get
Single-segment methods (e.g. ping, health) MAY be treated as plain JSON-RPC 2.0 and routed via legacy dispatch. Three-or-more-segment methods that do not match <resource>.<subresource>.<verb> SHOULD be rejected with -32600.
15. Security Considerations
Servers MUST validate:
- Allowed resources
- Allowed verbs
- Target ownership
- Params schema
Servers SHOULD NOT trust client-supplied meta.
Receivers SHOULD validate that request_id in async result messages references a known outstanding request before dispatching.
16. Versioning
RO-JRPC does not alter jsonrpc. Requests MUST continue using:
"jsonrpc": "2.0"
Protocol capability MAY be advertised through rpc.describe or transport negotiation.
17. Rationale
JSON-RPC method strings commonly encode structure informally:
user.create
task.cancel
tool.execute
RO-JRPC standardizes this widespread convention into explicit, machine-readable fields — enabling routing, authorization, and tooling that do not require method string parsing.
18. Caching
Both client and server MAY maintain their own cache independently. No negotiation is required for the basic case.
The natural cache key is: resource + verb + target (plus subresource + parent when present). Verbs get and list are cacheable by nature; create, update, and delete are not. Implementations SHOULD invalidate cached entries for a resource when a mutating verb (create, update, delete) is observed for that resource.
18.1 Cache Directive on Responses
Servers MAY include a cache field on responses to hint TTL to clients:
{
"jsonrpc": "2.0",
"result": { "id": "42", "name": "Alice" },
"cache": { "max-age": 60 },
"id": 1
}
| Field | Type | Meaning |
|---|---|---|
max-age |
integer |
Seconds the result may be reused by the client |
18.2 Cache Directive on Requests
Clients MAY include a cache field on requests to bypass the server cache:
{
"jsonrpc": "2.0",
"method": "user.get",
"resource": "user",
"verb": "get",
"target": "42",
"cache": "no-cache",
"id": 1
}
| Value | Meaning |
|---|---|
no-cache |
Server SHOULD NOT serve a cached result |
Implementations MAY extend the cache object with additional directives. Unrecognised directives MUST be ignored.
19. Async Results
RO-JRPC supports async result delivery through resource-oriented result messages. Whether delivery is push or pull, and whether the originating request is sync or async, is left to the implementation and transport.
19.1 Async Result Verbs
Two reserved verbs are defined for result messages flowing from server to client:
| Verb | Meaning |
|---|---|
yield |
Intermediate result; more messages expected |
return |
Final result; no further messages will be sent |
These verbs SHOULD NOT be used in client-to-server requests.
19.2 Result Message Structure
An async result message is itself a resource-oriented RO-JRPC message. The resource in the result does not need to match the resource of the originating request — for example, a long-running operation initiated on any resource may deliver results as a job resource.
The id field in the result message SHOULD match the id of the originating request when the result is delivered on the same connection or channel. When delivered on a different channel, or when there is no single originating request (pure event-driven), request_id MAY be used instead.
19.3 request_id
request_id is an optional field that explicitly correlates an async result back to the originating request. It SHOULD be included when:
- The result is delivered on a different channel or connection from the original request
- The result resource differs from the request resource (e.g.
job.yieldin response toattachment.create)
request_id MAY be omitted for pure event-driven cases where no single originating request exists, such as session events scoped by target.
19.4 Examples
Intermediate result (upload attachment → job):
{
"jsonrpc": "2.0",
"method": "job.yield",
"resource": "job",
"verb": "yield",
"target": "job-123",
"result": { "status": "pending", "progress": 60, "stage": "scanning" },
"request_id": "req-001"
}
Final result (upload attachment → job):
{
"jsonrpc": "2.0",
"method": "job.return",
"resource": "job",
"verb": "return",
"target": "job-123",
"result": { "status": "done", "attachmentId": "att-456" },
"request_id": "req-001"
}
Session event (pure event-driven, no request_id):
{
"jsonrpc": "2.0",
"method": "session.yield",
"resource": "session",
"verb": "yield",
"target": "session-9",
"result": { "status": "pending", "content": "Thinking..." }
}
Session final result:
{
"jsonrpc": "2.0",
"method": "session.return",
"resource": "session",
"verb": "return",
"target": "session-9",
"result": { "status": "done", "content": "Here is your answer." }
}
19.5 Status Field
The result object SHOULD include a status field when using async result verbs:
| Status | Meaning |
|---|---|
accepted |
Request received and queued, not yet started |
pending |
In progress, more results expected |
done |
Completed successfully |
error |
Failed; no further messages will be sent |
status is a convention within result — it is not a top-level envelope field.
20. Example: AI Agent Usage
{
"jsonrpc": "2.0",
"method": "tool.execute",
"resource": "tool",
"target": "web-search",
"verb": "execute",
"params": { "query": "rust async" },
"id": "req1"
}
21. Example: CLI Usage
{
"jsonrpc": "2.0",
"method": "build.execute",
"resource": "build",
"verb": "execute",
"params": { "target": "linux" },
"id": "1"
}
Top comments (0)