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.


Protocol overview

Key principles

  1. Single endpoint — All method calls go to a single HTTP endpoint (e.g., /rpc)
  2. HTTP POST — Use POST with JSON request body
  3. Two-level error handling — Protocol-level errors and execution-level errors
  4. 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 id from request
  • Must include result field (even if null)
  • MUST NOT include error field
  • 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

CodeMeaning
-32700Parse error
-32600Invalid Request
-32601Method not found
-32602Invalid params
-32603Internal 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

LevelLocationUse case
1Top-level errorRPC protocol issues
2Inside result.errorSkill/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

  1. Always check for error field first — Indicates protocol-level problem

  2. Then check result.status — For execution-level success/failure

  3. Preserve request ID — Use it to correlate responses in async scenarios

  4. Handle both error levels — Different handling for RPC vs execution errors

  5. 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

CodeMeaning
200RPC request processed (result or error)
400Malformed JSON
404Wrong endpoint
500Server crash (avoid if possible)

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.


Next Steps

Was this page helpful?