examples/stories/custom_methods/README.md
Register and call a vendor-prefixed JSON-RPC method that is not part of the
MCP spec. The server uses the low-level Server.add_request_handler (there is
no MCPServer surface for this, so server.py is lowlevel-native and there is
no server_lowlevel.py sibling); the client drops to client.session to send
it.
# stdio (default — the client spawns the server as a subprocess)
uv run python -m stories.custom_methods.client
# HTTP — the client self-hosts the server on a free port, runs, then tears it down
uv run python -m stories.custom_methods.client --http
client.py main — the body opens with Client(target, mode=mode). The
vendor request rides whichever protocol era mode selects; nothing else in
the story changes between eras.server.py SearchParams — subclasses types.RequestParams so _meta
(and on a 2026-07-28 connection, the reserved io.modelcontextprotocol/*
envelope keys) parse uniformly without extra code.server.py add_request_handler("acme/search", SearchParams, search) — the
method string is the wire method; use a vendor prefix so it can never
collide with a future spec method.client.py client.session.send_request(...) — Client only exposes spec
verbs, so vendor methods go through the underlying ClientSession.
send_request accepts any types.Request subclass.acme/searchProgress). The Python client can observe
vendor notifications via NotificationBinding (see
docs/advanced/extensions.md). That half is omitted here because the
lowlevel server has no surface for emitting vendor notifications yet.Requests — basic protocol (JSON-RPC request shape; vendor method names live outside the spec's reserved set).
serve_one/ (the per-exchange driver that runs registered handlers),
middleware/ (wrapping every registered handler, including vendor methods).