A JSON-RPC-based protocol is a lightweight, standardized way for different software systems (like apps, servers, or AI models) to communicate by sending and receiving structured messages in JSON format. The “JSON-RPC” part means the messages follow a specific format (JSON) and structure (RPC, or Remote Procedure Call), ensuring clarity and consistency.
Why JSON-RPC? Plain HTTP or TCP refers to lower-level transport protocols that carry data between systems. While JSON-RPC can (and often does) run over HTTP or WebSocket (which uses TCP), using raw HTTP or TCP without JSON-RPC’s structure would be problematic.
HTTP: HTTP is great for stateless, resource-based APIs (e.g., REST, where you GET /users/123 to fetch data). But it doesn’t natively support function-call semantics like “invoke this specific tool with these parameters.” You’d have to define custom endpoints (e.g., /move-to-position) for every tool, which gets messy for your “all-kinds” server with many tools. TCP: Raw TCP is even lower-level, just a stream of bytes with no inherent structure. You’d need to invent your own message format, parsing logic, and error handling from scratch—recreating what JSON-RPC already provides.
It’s worth noting the three layers:
- Application Layer (HTTP): Your browser formats a request for the webpage. HTTP (web), WebSocket (real-time), or SMTP (email)
- Transport Layer (TCP): Breaks the request into packets, ensures they arrive reliably.
- Network Layer (IP): Routes those packets across the internet to the server.
Robots often use WebSocket for real-time communication because it provides a persistent, bidirectional, low-latency connection over TCP, which is ideal for dynamic, time-sensitive tasks like controlling robotic movements, streaming sensor data, or coordinating actions.
websocket application in robot on server end is like
import asyncio
import websockets
async def echo(websocket, path):
async for message in websocket:
print(f"Received: {message}")
await websocket.send(f"Echo: {message}")
async def main():
server = await websockets.serve(echo, "localhost", 8765)
print("WebSocket server running on ws://localhost:8765")
await server.wait_closed()
if __name__ == "__main__":
asyncio.run(main())
on client end is
import websocket
import time
def on_message(ws, message):
print(f"Server response: {message}")
def on_error(ws, error):
print(f"Error: {error}")
def on_close(ws, close_status_code, close_msg):
print("Connection closed")
def on_open(ws):
print("Connected to server")
# Send a test message
ws.send("Hello, WebSocket!")
if __name__ == "__main__":
ws = websocket.WebSocketApp("ws://localhost:8765",
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.run_forever()
the Anthropic MCP server library typically embeds WebSocket support for JSON-RPC communication, using it as the default transport for real-time, bidirectional interactions.
Communication layers compare:
- DDS (RTPS) → real-time robotics middleware (UDP/TCP, binary, ultra-low latency)
- Kafka protocol → distributed, durable messaging (TCP, binary, high-throughput)
- WebSocket → persistent browser/server channel (TCP, framed messages)
- HTTP → universal request/response (TCP, text-based, highest overhead)
API Style Comparison: gRPC vs JSON-RPC vs REST
| Feature / Aspect | gRPC 🚀 | JSON-RPC 🌐 | REST 🌍 |
|---|---|---|---|
| Transport | HTTP/2 (binary framing, multiplexed streams) | Typically HTTP/1.1 (but transport-agnostic) | HTTP/1.1 (standard verbs: GET, POST, PUT…) |
| Encoding | Protocol Buffers (binary, compact) | JSON (text, verbose) | JSON / XML / YAML (usually JSON today) |
| Schema / Contract | Strongly typed (.proto schema required) | No schema (free-form JSON) | No enforced schema (can use OpenAPI/Swagger) |
| Performance | 🚀 High (small payload, fast parsing) | ⚡ Medium/Low (JSON overhead) | ⚡ Medium (JSON overhead, HTTP/1.1 limits) |
| Streaming | ✅ Yes (client, server, bidirectional) | ❌ No (request-response only) | ❌ No (request-response only) |
| Ease of Use | Requires tooling (codegen, .proto files) | Very simple (just JSON messages) | Simple (just HTTP + JSON) |
| Readability | ❌ Hard (binary messages not human-readable) | ✅ Easy (plain JSON) | ✅ Easy (plain JSON or XML) |
| Features | Deadlines, cancellations, load balancing, TLS | Minimal, just method/params/results/errors | Rich ecosystem: caching, proxies, auth |
| Error Handling | Status codes + error messages | JSON error objects | HTTP status codes + JSON/XML error body |
| Best For | Microservices, high-throughput APIs, streaming | Blockchain APIs, simple RPC-based calls | Public APIs, CRUD services, web integration |
| Examples | Google Cloud APIs, Kubernetes, Istio | Ethereum JSON-RPC, Bitcoin RPC | Twitter API, GitHub API, RESTful web services |
- Style: Resource-based (everything is a thing you interact with).
- Transport: Always HTTP.
- Format: Usually JSON (but can be XML, etc.).
- How it looks: CRUD
but gRPC
- Style: Function call based (like JSON-RPC, but more structured).
- Transport: HTTP/2 (binary protocol).
- Format: Protocol Buffers (Protobuf), a compact binary format.
- How it looks (IDL in
.protofile):
