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-mcp centers on the callTool function in graph-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 RpcError class.
  • 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →