Bidirectional JSON-RPC 2.0 over WebSocket β TypeScript & Python.
Note: This project is under active development. APIs may change before v1.0.
# TypeScript
pnpm add echorpc
# Python
pip install echorpc- Bidirectional RPC β server and client can call each other's methods
- Pub/Sub β fire-and-forget notifications via
publish/subscribe - Batch requests β multiple calls per frame, results returned in order
- Auth β token validated during HTTP upgrade
- Heartbeat β ping/pong with auto-disconnect on timeout
- Auto-reconnect β exponential backoff, handlers and subscriptions preserved
- Broadcast β send to all connections, filter by role, or exclude specific peers
from echorpc import EchoServer
server = EchoServer(port=9100, auth_handler=lambda p: p["token"] == "secret")
# Define a command that clients can call
@server.command("add")
def add(params):
return {"sum": params["a"] + params["b"]}
# Listen for events from clients
@server.event("chat")
async def on_chat(data):
print("on_chat", data)
await server.start()from echorpc import EchoClient
client = EchoClient("ws://localhost:9100", token="secret")
# Let the server call you back
client.register("double", lambda p: p["x"] * 2)
# Subscribe to events
client.subscribe("chat", lambda data: print(data))
await client.connect()
# Call a server command
result = await client.request("add", {"a": 1, "b": 2})
# Publish event to the server
await client.publish("chat", {"text": "hello"})
# Send multiple calls at once
results = await client.batch_request([
("add", {"a": 1, "b": 2}),
("add", {"a": 3, "b": 4}),
])import { EchoServer } from "echorpc";
const server = new EchoServer({
port: 9100,
authHandler: (p) => p.token === "secret",
});
// Define a command that clients can call
server.register("add", (p: { a: number; b: number }) => ({
sum: p.a + p.b,
}));
// Listen for events from clients
server.subscribe("chat", async (data) => {
console.log("chat", data);
});
await server.start();import WebSocket from "ws";
import { EchoClient } from "echorpc";
const client = new EchoClient("ws://localhost:9100", {
token: "secret",
WebSocket,
});
// Let the server call you back
client.register("double", (p) => p.x * 2);
// Subscribe to events
client.subscribe("chat", (data) => console.log(data));
await client.connect();
// Call a server command
const result = await client.request("add", { a: 1, b: 2 });
// Publish event to the server
client.publish("chat", { text: "hello" });
// Send multiple calls at once
const results = await client.batchRequest([
["add", { a: 1, b: 2 }],
["add", { a: 3, b: 4 }],
]);No WebSocket import needed β uses the native one.
import { EchoClient } from "echorpc";
const client = new EchoClient("ws://localhost:9100", { token: "secret" });
await client.connect();| Code | Name | Meaning |
|---|---|---|
| -32700 | Parse error | Invalid JSON |
| -32601 | Method not found | No such method |
| -32603 | Internal error | Handler threw |
| -32001 | Not connected | No active connection |
| -32002 | Timeout | Request timed out |
| -32003 | Auth failed | Authentication rejected |
MIT License Β© 2026-PRESENT Del Wang