Python’s xmlrpc.server module creates HTTP servers that expose callable functions and methods as XML-RPC endpoints, enabling language-neutral RPC. from xmlrpc.server import SimpleXMLRPCServer, MultiPathXMLRPCServer. Create: server = SimpleXMLRPCServer(("localhost", 8000), allow_none=True, logRequests=False). Register function: server.register_function(fn, name="math.add") — fn becomes callable as math.add via RPC; omit name to use fn.__name__. Register instance: server.register_instance(obj) — all public (non-_ prefixed) methods of obj become RPC methods; set allow_dotted_names=False (the default) to prevent attribute traversal. Enable introspection: server.register_introspection_functions() → adds system.listMethods, system.methodSignature, system.methodHelp. Enable multicall: server.register_multicall_functions() → adds system.multicall for batching. Serve: server.serve_forever() / server.handle_request(). Multi-path: MultiPathXMLRPCServer(addr) with server.add_dispatcher("/rpc/math", math_dispatcher). CGI: CGIXMLRPCRequestHandler().handle_request() for Apache/nginx CGI deployments. Thread safety: subclass socketserver.ThreadingMixIn + SimpleXMLRPCServer for concurrent requests. Claude Code generates REST-free RPC services, calculator APIs, test harnesses, and multi-service XML-RPC routers.
CLAUDE.md for xmlrpc.server
## xmlrpc.server Stack
- Stdlib: from xmlrpc.server import SimpleXMLRPCServer
- Create: server = SimpleXMLRPCServer(("0.0.0.0", 8000), allow_none=True)
- Reg fn: server.register_function(fn) # uses fn.__name__
- server.register_function(fn, "ns.method")
- Reg obj: server.register_instance(MyService()) # all public methods
- Intro: server.register_introspection_functions()
- Serve: server.serve_forever() (blocking)
- server.handle_request() (one shot, for testing)
xmlrpc.server XML-RPC Server Pipeline
# app/xmlrpcutil.py — service class, threading, dispatch, client test, auth
from __future__ import annotations
import hashlib
import hmac
import socketserver
import threading
import time
import xmlrpc.client
from http.server import BaseHTTPRequestHandler
from typing import Any
from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
# ─────────────────────────────────────────────────────────────────────────────
# 1. Service classes
# ─────────────────────────────────────────────────────────────────────────────
class MathService:
"""
Simple arithmetic service exposed over XML-RPC.
RPC methods: add, subtract, multiply, divide, power, factorial
"""
def add(self, a: float, b: float) -> float:
"""Add two numbers. >>> add(2, 3) → 5"""
return a + b
def subtract(self, a: float, b: float) -> float:
"""Subtract b from a."""
return a - b
def multiply(self, a: float, b: float) -> float:
"""Multiply two numbers."""
return a * b
def divide(self, a: float, b: float) -> float:
"""Divide a by b. Raises xmlrpc.client.Fault if b == 0."""
if b == 0:
raise xmlrpc.client.Fault(1, "Division by zero")
return a / b
def power(self, base: float, exp: float) -> float:
"""Return base ** exp."""
return base ** exp
def factorial(self, n: int) -> int:
"""Return n! for non-negative integer n."""
if not isinstance(n, int) or n < 0:
raise xmlrpc.client.Fault(2, "n must be a non-negative integer")
result = 1
for i in range(2, n + 1):
result *= i
return result
class EchoService:
"""
Utility service: echo, ping, info, type_of.
"""
def __init__(self, server_name: str = "xmlrpc-demo"):
self._name = server_name
self._start = time.time()
def ping(self) -> str:
"""Return 'pong'."""
return "pong"
def echo(self, value: Any) -> Any:
"""Return the value as-is."""
return value
def info(self) -> dict:
"""Return server metadata."""
return {
"name": self._name,
"uptime_seconds": round(time.time() - self._start, 2),
"python_xmlrpc": "xmlrpc.server",
}
def type_of(self, value: Any) -> str:
"""Return the type name of the given value."""
return type(value).__name__
# ─────────────────────────────────────────────────────────────────────────────
# 2. Server factory
# ─────────────────────────────────────────────────────────────────────────────
class ThreadedXMLRPCServer(socketserver.ThreadingMixIn, SimpleXMLRPCServer):
"""Thread-per-request XML-RPC server."""
daemon_threads = True
allow_reuse_address = True
def make_server(
host: str = "127.0.0.1",
port: int = 8000,
allow_none: bool = True,
log_requests: bool = False,
threaded: bool = True,
) -> SimpleXMLRPCServer:
"""
Create a (optionally threaded) SimpleXMLRPCServer with introspection + multicall.
Example:
server = make_server(port=9000)
server.register_instance(MathService())
server.handle_request() # one shot for testing
"""
cls = ThreadedXMLRPCServer if threaded else SimpleXMLRPCServer
server = cls(
(host, port),
allow_none=allow_none,
logRequests=log_requests,
)
server.register_introspection_functions()
server.register_multicall_functions()
return server
def serve_in_thread(
server: SimpleXMLRPCServer,
) -> threading.Thread:
"""
Run server.serve_forever() in a daemon thread.
Returns the thread object; call server.shutdown() to stop.
Example:
server = make_server(port=9001)
server.register_instance(EchoService())
t = serve_in_thread(server)
# ... do work ...
server.shutdown()
"""
t = threading.Thread(target=server.serve_forever, daemon=True)
t.start()
return t
# ─────────────────────────────────────────────────────────────────────────────
# 3. Client helper
# ─────────────────────────────────────────────────────────────────────────────
def make_client(
host: str = "127.0.0.1",
port: int = 8000,
path: str = "/RPC2",
allow_none: bool = True,
timeout: int = 10,
) -> xmlrpc.client.ServerProxy:
"""
Create an xmlrpc.client.ServerProxy connected to the given server.
Example:
client = make_client(port=9000)
result = client.add(3, 4)
methods = client.system.listMethods()
"""
uri = f"http://{host}:{port}{path}"
return xmlrpc.client.ServerProxy(
uri, allow_none=allow_none, use_datetime=True
)
def multicall(
client: xmlrpc.client.ServerProxy,
calls: list[tuple[str, list]],
) -> list[Any]:
"""
Execute multiple RPC calls in one HTTP request using system.multicall.
Example:
results = multicall(client, [
("add", [1, 2]),
("multiply", [3, 4]),
("ping", []),
])
"""
mc = xmlrpc.client.MultiCall(client)
for method_name, args in calls:
getattr(mc, method_name)(*args)
return list(mc())
# ─────────────────────────────────────────────────────────────────────────────
# 4. Multi-service dispatcher
# ─────────────────────────────────────────────────────────────────────────────
def register_services(
server: SimpleXMLRPCServer,
services: dict[str, object],
prefix_separator: str = ".",
) -> None:
"""
Register multiple service instances under dotted-prefix namespaces.
Each public method of obj is registered as "prefix.method_name".
Example:
register_services(server, {
"math": MathService(),
"echo": EchoService(),
})
# client.math.add(1, 2), client.echo.ping()
"""
for prefix, obj in services.items():
for name in dir(obj):
if name.startswith("_"):
continue
fn = getattr(obj, name)
if callable(fn):
rpc_name = f"{prefix}{prefix_separator}{name}"
server.register_function(fn, rpc_name)
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
import socket
print("=== xmlrpc.server demo ===")
# ── Find a free port ──────────────────────────────────────────────────────
with socket.socket() as s:
s.bind(("127.0.0.1", 0))
port = s.getsockname()[1]
# ── Start server ──────────────────────────────────────────────────────────
server = make_server(port=port, log_requests=False)
register_services(server, {
"math": MathService(),
"echo": EchoService("demo-server"),
})
t = serve_in_thread(server)
time.sleep(0.1) # let thread start
print(f"\n Server listening on 127.0.0.1:{port}")
# ── Client calls ──────────────────────────────────────────────────────────
client = make_client(port=port)
print("\n--- individual calls ---")
print(f" math.add(3, 4) = {client.math.add(3, 4)}")
print(f" math.multiply(6, 7) = {client.math.multiply(6, 7)}")
print(f" math.factorial(10) = {client.math.factorial(10)}")
print(f" echo.ping() = {client.echo.ping()!r}")
print(f" echo.type_of([1,2,3]) = {client.echo.type_of([1, 2, 3])!r}")
try:
client.math.divide(1, 0)
except xmlrpc.client.Fault as e:
print(f" divide(1,0) fault: {e.faultCode} {e.faultString!r}")
# ── Introspection ─────────────────────────────────────────────────────────
print("\n--- system.listMethods() ---")
methods = client.system.listMethods()
for m in sorted(methods):
print(f" {m}")
# ── Multicall ─────────────────────────────────────────────────────────────
print("\n--- multicall ---")
results = multicall(client, [
("math.add", [10, 20]),
("math.power", [2, 8]),
("echo.ping", []),
])
for (name, _), result in zip(
[("math.add", []), ("math.power", []), ("echo.ping", [])], results
):
print(f" {name}: {result}")
# ── Shutdown ──────────────────────────────────────────────────────────────
server.shutdown()
t.join(timeout=2)
print("\n=== done ===")
For the Flask / FastAPI (PyPI) alternatives — modern REST or JSON-RPC APIs built with Flask @app.route or FastAPI @app.post("/rpc") provide typed schemas, automatic OpenAPI docs, middleware, authentication decorators, and async support — use Flask or FastAPI for any new service; use xmlrpc.server for legacy integrations, compatibility with existing XML-RPC clients (WordPress, Confluence, Bugzilla, many monitoring tools), zero-dependency service exposure in constrained environments, and CLI tooling that already uses xmlrpc.client. For the rpyc (PyPI) alternative — rpyc.connect(host, port) provides Python-native object-level RPC including streaming, callbacks, and transparent remote attribute access — use rpyc when both client and server are Python and you want richer calling semantics; use xmlrpc.server when clients may be PHP, Ruby, Java, or any language with an XML-RPC library, since the protocol is a widely-supported cross-language standard. The Claude Skills 360 bundle includes xmlrpc.server skill sets covering MathService/EchoService service classes, ThreadedXMLRPCServer with make_server()/serve_in_thread() factories, make_client()/multicall() client helpers, and register_services() multi-namespace dispatcher. Start with the free tier to try XML-RPC server patterns and xmlrpc.server pipeline code generation.