JSON-RPC 2.0 Protocol Implementation for MCP Communication in codebase-memory-mcp
The codebase-memory-mcp repository implements a lightweight JSON-RPC 2.0 client in TypeScript that communicates with the Memory-Control-Panel backend via HTTP POST requests to the /rpc endpoint, handling request serialization, error propagation, and result unmarshalling through a single callTool() function.
The codebase-memory-mcp project provides a complete JSON-RPC 2.0 client implementation that enables the frontend graph interface to communicate with the MCP (Memory-Control-Panel) backend. Located in graph-ui/src/api/rpc.ts, this implementation manages the full request lifecycle from payload construction to response parsing while maintaining strict compliance with the JSON-RPC 2.0 specification.
Request Structure and Payload Construction
In graph-ui/src/api/rpc.ts, the callTool function constructs JSON-RPC 2.0 requests according to the protocol specification. Each request includes four required fields: the protocol version, a unique identifier, the method name, and parameters.
Protocol Version and Method Specification
The implementation explicitly sets the jsonrpc field to "2.0" to ensure compliance with the JSON-RPC 2.0 specification. Method names follow MCP-specific conventions, such as "tools/call", which the backend recognizes as tool invocation requests.
body: JSON.stringify({
jsonrpc: "2.0", // Protocol version identifier
id: _nextId++, // Auto-incrementing request ID
method: "tools/call", // MCP-specific method endpoint
params: { name, arguments: args },
})
Request ID Management
The client maintains an internal counter _nextId that auto-increments for each request, ensuring every JSON-RPC message carries a unique identifier. This enables the client to match responses to specific requests when handling concurrent operations.
HTTP Transport Configuration
Requests are transmitted via HTTP POST to the /rpc endpoint with a Content-Type: application/json header. This transport mechanism bridges the JSON-RPC protocol over standard HTTP, allowing the frontend to communicate with the backend service.
Error Handling and Response Validation
The implementation distinguishes between transport-level failures and JSON-RPC protocol errors through a two-stage validation process defined in lines 30-38 of rpc.ts.
HTTP Status Verification
After executing the fetch request, the client first checks the HTTP response status. If the status indicates failure (not OK), the client throws an RpcError with a generic code of -1, signaling a network or transport layer problem.
JSON-RPC Error Processing
For successful HTTP responses, the client parses the JSON body and checks for the presence of an error object. When present, the client throws an RpcError populated with the specific code and message supplied by the server, preserving the backend's error context.
// Error handling pattern from rpc.ts
if (!response.ok) {
throw new RpcError("HTTP error", -1);
}
const data = await response.json();
if (data.error) {
throw new RpcError(data.error.message, data.error.code);
}
RpcError Class Architecture
The RpcError class extends JavaScript's native Error to include an RPC-specific error code property. This design allows calling code to distinguish between protocol-level failures, application errors, and network interruptions programmatically.
Result Unmarshalling and Data Extraction
MCP tool results arrive wrapped in a nested structure containing a content array with text elements. According to lines 40-47 of rpc.ts, the client extracts the first text field from result.content[0].text and automatically parses it as JSON when present, otherwise returning the raw result object.
// Result extraction logic from lines 40-47 of rpc.ts
const text = data.result?.content?.[0]?.text;
if (text !== undefined) {
return JSON.parse(text);
}
return data.result;
This unmarshalling strategy handles MCP's convention of wrapping tool outputs in content objects while providing developers with clean, parsed data structures.
Practical Implementation Examples
The following examples demonstrate how to interact with the JSON-RPC 2.0 implementation using the callTool function.
Fetching Structured Data
Call a tool that returns a plain JSON object by specifying the expected TypeScript interface:
interface ProjectList {
projects: string[];
}
const projects = await callTool<ProjectList>("projects/list");
console.log(projects.projects);
Handling Stringified JSON Payloads
When tools return stringified JSON within the text content field, the client automatically parses the inner payload:
type Summary = { title: string; body: string };
const summary = await callTool<Summary>("summary/generate", {
text: "Explain JSON-RPC"
});
console.log(`${summary.title}: ${summary.body}`);
Supporting hooks like useProjects.ts and useGraphData.ts demonstrate production usage patterns for populating UI state with RPC data.
Summary
- The JSON-RPC 2.0 protocol implementation in
codebase-memory-mcpcenters on thecallToolfunction ingraph-ui/src/api/rpc.ts. - Request construction follows strict JSON-RPC 2.0 standards with auto-incrementing IDs and proper method naming conventions.
- Error handling differentiates HTTP transport failures from JSON-RPC protocol errors using the
RpcErrorclass. - Result unmarshalling automatically extracts and parses JSON content from MCP's nested response structure.
- The implementation provides type-safe integration between the frontend graph interface and the MCP backend.
Frequently Asked Questions
How does the codebase-memory-mcp client ensure JSON-RPC 2.0 compliance?
The client enforces compliance by explicitly setting the jsonrpc field to "2.0", including unique request IDs through auto-incrementing counters, and structuring requests with the required method and params fields. This implementation adheres to the official JSON-RPC 2.0 specification for single-request messaging.
What error codes does the JSON-RPC implementation use?
The implementation uses code -1 for HTTP transport failures (non-OK responses), while server-side JSON-RPC errors pass through with their original codes as defined in the MCP backend. The RpcError class captures both scenarios, allowing applications to handle network issues separately from business logic errors.
Where is the JSON-RPC client code located in the repository?
The core implementation resides in graph-ui/src/api/rpc.ts, which contains the callTool function and RpcError class. Supporting type definitions appear in graph-ui/src/lib/types.ts, while usage examples exist in graph-ui/src/hooks/useProjects.ts and graph-ui/src/hooks/useGraphData.ts.
How does the client handle MCP tool results that contain stringified JSON?
The client automatically inspects the result.content[0].text field when present, parsing the string as JSON and returning the resulting object. If the text field is undefined, the client returns the raw result object, providing flexibility for tools that return different payload structures.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →