Skip to content

Add reusable JSON-RPC 2.0 server library (synapse.lib.jsrpc)#4967

Draft
invisig0th wants to merge 11 commits into
masterfrom
jsrpc
Draft

Add reusable JSON-RPC 2.0 server library (synapse.lib.jsrpc)#4967
invisig0th wants to merge 11 commits into
masterfrom
jsrpc

Conversation

@invisig0th
Copy link
Copy Markdown
Contributor

Summary

Adds synapse/lib/jsrpc.py, a generic, reusable JSON-RPC 2.0 server that exposes an arbitrary Python object (with decorated methods carrying metadata) at a URL on the existing Tornado web server. It is intentionally protocol-agnostic; future work (e.g. an MCP server) can be built on top of it as a consumer.

Details

  • @s_jsrpc.method(name=, desc=, params=, returns=, perm=) — opt-in decorator + rich metadata. The name override allows non-identifier method names (e.g. tools/list).
  • getMethInfo() / descrMethods() — reusable introspection over the decorated methods (instance-cached), for building discovery layers later.
  • Handler(s_httpapi.Handler) — a single Tornado handler doing parse + dispatch + HTTP. Mounted via cell.addHttpApi(path, s_jsrpc.Handler, {'cell':..., 'item':obj}).
    • Supports single requests, batch, notifications (HTTP 204), and async-generator streaming over SSE (when the client sends Accept: text/event-stream; otherwise generator output is collected into an array).
    • Reuses existing handler auth (reqAuthUser()), enforces optional per-method permissions, and wraps dispatch in with s_scope.enter({'user': user}) so methods recover the caller via s_scope.get('user').
  • Error model: reserved codes for protocol errors; s_exc.JsonRpcError(code=, mesg=, data=) (new) for application-defined errors; any other uncaught exception maps to -32603 Internal error with SynErr errinfo attached as error.data (JSON-safety guarded).

Testing

  • synapse/tests/test_lib_jsrpc.py — covers metadata/introspection, by-position & by-name params, all error codes, per-method perms (allow/deny/grant), notifications, batch (incl. empty + all-notifications), and SSE streaming (success + mid-stream failure). 100% coverage of the new module.

Notes

  • No default route is registered; this is internal API only, so no user docs or changelog entry yet (deferred until a consumer exposes it).

Add a generic JSON-RPC 2.0 server that exposes a Python object with
@s_jsrpc.method-decorated methods at a URL on the Tornado web server.
Supports batch, notifications, and async-generator streaming via SSE,
reuses s_httpapi.Handler auth with per-method perms, and carries the
calling user via s_scope. Adds s_exc.JsonRpcError for app-defined errors.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 30, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.78%. Comparing base (186ba33) to head (2bc0d91).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4967      +/-   ##
==========================================
- Coverage   97.81%   97.78%   -0.04%     
==========================================
  Files         299      301       +2     
  Lines       63433    64126     +693     
==========================================
+ Hits        62047    62703     +656     
- Misses       1386     1423      +37     
Flag Coverage Δ
linux 97.71% <100.00%> (-0.04%) ⬇️
linux_replay 93.57% <100.00%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Users now extend JsonRpcHandler and implement @s_jsrpc.method decorated
methods directly, rather than passing a shared item. The getMethInfo and
descrMethods helpers move onto the class as classmethods (introspecting
the class and caching per-class).
Add CellMcp, a Model Context Protocol server handler built on the jsrpc
JsonRpcHandler, providing the MCP lifecycle (initialize/initialized),
stateful user-bound sessions (Mcp-Session-Id), Bearer-as-API-key auth, an
@s_mcp.tool decorator + registry, and tools/list + tools/call with SSE
streaming for async generator tools. Add CortexMcp with storm, callStorm,
and getModel tools.

Cells opt in by setting the _mcp_ctor class attribute, which the base Cell
mounts at /api/v1/mcp during HTTP init; Cortex sets it to CortexMcp.
Extend the MCP server with the remaining server capabilities, reusing the
decorator + class-cached-registry pattern:
- @s_mcp.resource (static + {var} templates) -> resources/list,
  resources/templates/list, resources/read (text/blob/json contents).
- @s_mcp.prompt -> prompts/list, prompts/get (str or message-list).
- @s_mcp.completer + completion/complete for ref/prompt and ref/resource.
- logging/setLevel + notifications/message gated on streamed tool output
  via an overridable _streamItemLevel hook.

Capabilities are advertised dynamically based on non-empty registries.
CortexMcp adds syn://model, syn://stormdocs, syn://model/form/{name}
resources, a storm-query prompt, and form/type name completers; CellMcp
adds a syn://cellinfo resource.
Tools, resources, and prompts no longer declare a perm on the decorator;
a method that requires permissions enforces them itself in its body. Drops
the decorator perm enforcement and the _reqPerm helper.
The jsrpc @method and mcp @tool/@resource/@prompt/@completer decorators now
reject non-async functions at decoration time (raise BadArg), so dispatch
awaits unconditionally instead of checking inspect.isawaitable at runtime.
Drops the per-method permission declaration and its enforcement in
_dispatch (and the now-unused ACCESS_DENIED constant), consistent with the
MCP decorators. Methods that require permissions enforce them themselves.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant