JSON-RPC Conventions
The Skills Protocol uses JSON-RPC 2.0 over HTTP as its transport layer. This document covers the conventions for request/response formatting and error handling.
This specification follows JSON-RPC 2.0 with Skills Protocol-specific conventions for skill execution errors.
Protocol overview
Key principles
- Single endpoint — All method calls go to a single HTTP endpoint (e.g.,
/rpc) - HTTP POST — Use POST with JSON request body
- Two-level error handling — Protocol-level errors and execution-level errors
- Stateless — Each request is independent; no session management
Transport
Method: POST
URL: http://localhost:8080/rpc
Content-Type: application/json
Request format
Standard JSON-RPC 2.0 request:
{
"jsonrpc": "2.0",
"method": "list_skills",
"params": {
"namespace": "salesforce"
},
"id": "1"
}
Fields
- Name
jsonrpc- Type
- string
- Required
- required
- Description
Always
"2.0"for JSON-RPC 2.0
- Name
method- Type
- string
- Required
- required
- Description
Method name (e.g.,
"list_skills")
- Name
params- Type
- object | array
- Description
Parameters for the method. Can be object (named) or array (positional). MVP uses objects only.
- Name
id- Type
- string | number | null
- Description
Request identifier. Required for requests expecting responses. Can be any type.
Example requests
List skills
{
"jsonrpc": "2.0",
"method": "list_skills",
"params": {
"namespace": "salesforce",
"limit": 50
},
"id": "1"
}
Execute skill
{
"jsonrpc": "2.0",
"method": "execute_skill",
"params": {
"name": "salesforce.leads.sync",
"args": {"env": "prod"}
},
"id": "2"
}
Minimal (no params)
{
"jsonrpc": "2.0",
"method": "load_skills_protocol_guide",
"params": {},
"id": "3"
}
Success responses
Successful response includes result field:
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"skills": [
{
"name": "salesforce.leads.sync",
"version": "0.1.0",
"description": "Sync leads..."
}
],
"next_cursor": null
}
}
Requirements
- Must include
jsonrpc: "2.0" - Must include matching
idfrom request - Must include
resultfield (even if null) - MUST NOT include
errorfield - HTTP status code: 200 OK
Examples
list_skills success
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"skills": [...]
}
}
execute_skill success
{
"jsonrpc": "2.0",
"id": "2",
"result": {
"status": "completed",
"run_id": "run_9f2a",
"output": {...}
}
}
Empty result
{
"jsonrpc": "2.0",
"id": "3",
"result": null
}
Error responses
Errors are indicated by an error field instead of result:
{
"jsonrpc": "2.0",
"id": "1",
"error": {
"code": -32602,
"message": "Invalid params",
"data": "Optional additional info"
}
}
Fields
- Name
code- Type
- integer
- Required
- required
- Description
Error code (see codes below)
- Name
message- Type
- string
- Required
- required
- Description
Short error description
- Name
data- Type
- any
- Description
Optional additional error information
Standard error codes
Protocol-level errors
| Code | Meaning |
|---|---|
| -32700 | Parse error |
| -32600 | Invalid Request |
| -32601 | Method not found |
| -32602 | Invalid params |
| -32603 | Internal error |
Examples
{
"jsonrpc": "2.0",
"id": "1",
"error": {
"code": -32601,
"message": "Method not found"
}
}
{
"jsonrpc": "2.0",
"id": "2",
"error": {
"code": -32602,
"message": "Invalid params: missing 'name'",
"data": {
"param": "name",
"reason": "required"
}
}
}
HTTP status
Protocol-level errors: HTTP 200 OK with error in JSON body
(Following JSON-RPC convention: HTTP success even if RPC failed)
Two-level error model
The Skills Protocol distinguishes between two types of errors:
Level 1: Protocol errors (top-level error)
Used for issues with the RPC call itself:
- Invalid JSON
- Missing required parameters
- Method not found
- Authorization failure
- Skill not found
{
"jsonrpc": "2.0",
"id": "1",
"error": {
"code": -32602,
"message": "Invalid params: skill 'foo.bar' not found"
}
}
Level 2: Execution errors (in result)
Used for errors that occur during skill/code execution within the sandbox:
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"status": "failed",
"run_id": "run_9f2a",
"summary": "Skill execution failed.",
"error": {
"type": "AuthenticationError",
"message": "Invalid Salesforce API key"
}
}
}
Key difference
| Level | Location | Use case |
|---|---|---|
| 1 | Top-level error | RPC protocol issues |
| 2 | Inside result.error | Skill/code execution failure |
Important: A successful RPC call (status: 200) may return result.status: "failed" if the skill itself failed.
Common patterns
Checking for success
response = requests.post('http://localhost:8080/rpc', json=payload)
data = response.json()
# Check for protocol-level error
if "error" in data:
print(f"RPC Error: {data['error']['message']}")
else:
# Check for execution-level error
result = data["result"]
if result.get("status") == "failed":
print(f"Execution failed: {result['error']['message']}")
else:
print(f"Success: {result}")
Error handling in Python client
def call_skill(name, args):
payload = {
"jsonrpc": "2.0",
"method": "execute_skill",
"params": {"name": name, "args": args},
"id": "1"
}
response = requests.post(endpoint, json=payload)
data = response.json()
# Level 1: Protocol error
if "error" in data:
raise ProtocolError(data["error"]["message"])
result = data["result"]
# Level 2: Execution error
if result.get("status") == "failed":
raise ExecutionError(result["error"]["message"])
return result
Best practices
Always check for
errorfield first — Indicates protocol-level problemThen check
result.status— For execution-level success/failurePreserve request ID — Use it to correlate responses in async scenarios
Handle both error levels — Different handling for RPC vs execution errors
Log full response — For debugging, capture complete request/response
HTTP considerations
Method
Use POST for all requests (no GET queries).
Headers
POST /rpc HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Content-Length: (size)
Status codes
| Code | Meaning |
|---|---|
| 200 | RPC request processed (result or error) |
| 400 | Malformed JSON |
| 404 | Wrong endpoint |
| 500 | Server crash (avoid if possible) |
Even when the RPC call fails (with error field), return HTTP 200. This follows JSON-RPC 2.0 convention.
Timeout handling
Implement reasonable timeouts:
response = requests.post(
endpoint,
json=payload,
timeout=30 # 30 second timeout for normal calls
)
For long-running executions, clients may want to increase timeout or implement polling.