Cuando hoy vemos un mensaje como este:

{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }

Puede parecer simplemente una convención para enviar objetos JSON entre dos programas.

Pero detrás de esas cuatro propiedades hay un protocolo con más de veinte años de historia: JSON-RPC.

JSON-RPC nació en los primeros años de la Web moderna, cuando XML dominaba los web services, SOAP prometía resolver prácticamente cualquier problema de comunicación entre sistemas y JSON todavía era una tecnología relativamente nueva.

Su objetivo era mucho más modesto: permitir que dos programas pudieran llamarse funciones mutuamente utilizando mensajes extremadamente sencillos.

Esa simplicidad explica en buena medida por qué sigue vivo dos décadas después y por qué protocolos modernos como Language Server Protocol (LSP) o Model Context Protocol (MCP) lo utilizan como capa de comunicación. LSP define sus peticiones, respuestas y notificaciones utilizando JSON-RPC, y MCP exige actualmente que sus mensajes sigan JSON-RPC 2.0.

Antes de JSON-RPC: llamar a una función que está en otra máquina

La idea de RPC es bastante anterior a la Web.

RPC significa Remote Procedure Call, o llamada a procedimiento remoto. La idea consiste en intentar que una operación ejecutada en otro proceso —y potencialmente en otra máquina— se parezca conceptualmente a una llamada normal a una función.

En lugar de pensar:

construir mensaje serializar argumentos abrir conexión enviar bytes esperar respuesta deserializar respuesta

el programador puede pensar aproximadamente en:

resultado = sumar(20, 22)

aunque sumar se esté ejecutando realmente en otro sistema.

El concepto se desarrolló durante las décadas de 1970 y 1980. Uno de los sistemas que ayudaron a popularizarlo fue Sun RPC, cuya versión 2 fue documentada en el RFC 1057 en 1988. Ese protocolo ya contenía ideas que reconoceríamos inmediatamente en JSON-RPC: identificar qué procedimiento queremos ejecutar, enviar parámetros y asociar cada respuesta con su petición mediante un identificador de transacción.

El problema era que aquellos protocolos estaban diseñados para un mundo muy distinto al de las aplicaciones web.

Entonces llegó HTTP.

XML-RPC y la era de XML

A finales de los años noventa surgió XML-RPC, desarrollado originalmente alrededor de UserLand y promovido por Dave Winer.

XML-RPC trasladaba la idea de RPC a tecnologías de Internet extremadamente extendidas: HTTP como transporte y XML para representar los mensajes.

Una llamada podía conceptualmente decir:

ejecuta el método sumar con los parámetros 20 y 22

y el servidor devolvería el resultado.

Era considerablemente más sencillo que muchas de las tecnologías de web services que aparecerían después, y su especificación pública existe desde 1998.

Pero seguía existiendo un problema: XML es bastante verboso.

Una operación muy pequeña requiere etiquetas de apertura, etiquetas de cierre, tipos y bastante estructura adicional.

Al mismo tiempo, las aplicaciones web estaban empezando a realizar cada vez más comunicación directamente desde JavaScript.

Y apareció una alternativa perfecta.

La llegada de JSON

Douglas Crockford comenzó a promover a principios de los años 2000 lo que acabaríamos conociendo como JSON, JavaScript Object Notation.

JSON utilizaba una notación inspirada en los objetos y arrays de JavaScript y ofrecía precisamente lo que necesitaban muchas aplicaciones web: una representación de datos pequeña, legible y extremadamente fácil de generar y procesar.

Crockford sitúa su propio descubrimiento y uso del formato en abril de 2001, cuando trabajaba como CTO de State Software, y registró json.org en 2002 para documentarlo.

Frente al enorme ecosistema XML de la época, JSON defendía una filosofía radicalmente diferente: hacer muy pocas cosas y hacerlas bien.

Esa idea acabaría siendo también fundamental para JSON-RPC.

12 de marzo de 2004: nace JSON-RPC

La propia web histórica del proyecto conserva una pequeña historia sobre su origen.

El 12 de marzo de 2004, durante una conversación sobre comunicación cliente-servidor en el canal IRC de SVG, Jim Ley sugirió utilizar JSON como esquema de codificación.

Jan-Klaas Kollhof, que hasta entonces era partidario de XML-RPC, empezó a experimentar con la idea e implementó su propio módulo JSON en JavaScript.

Entonces Robin Berjon hizo una observación extraordinariamente sencilla:

alguien debería definir JSON-RPC.

Robin Berjon

Aquella misma tarde, Kollhof empezó a hacerlo.

Así nació JSON-RPC.

No fue el resultado de un enorme comité de estandarización ni de una nueva arquitectura empresarial. Surgió de una conversación entre desarrolladores intentando encontrar una forma más sencilla de comunicar un navegador y un servidor.

La documentación y código histórico también vinculan directamente el proyecto con Jan-Klaas Kollhof, que continuó desarrollando implementaciones y en 2006 presentó en EuroPython una charla llamada “JSON-RPC — makes web services communication simple”. Allí describía precisamente sus objetivos: ser fácil de entender, fácil de implementar, basado en texto y suficientemente sencillo como para poder implementarse en JavaScript.

La filosofía puede resumirse prácticamente en la frase que todavía aparece en la página oficial:

It is designed to be simple!

¿Qué problema intentaba solucionar?

JSON-RPC no inventaba RPC.

Lo que hacía era reducirlo prácticamente a su mínima expresión.

Un protocolo RPC necesita resolver esencialmente cuatro preguntas:

¿Qué método quiero ejecutar?

"method": "sum"

¿Con qué argumentos?

"params": [20, 22]

¿A qué petición pertenece posteriormente una respuesta?

"id": 1

¿Cuál fue el resultado?

"result": 42

JSON ya proporcionaba todo lo necesario para representar esa información.

Por tanto, no hacía falta inventar otro lenguaje de serialización.

Una petición JSON-RPC 2.0 completa puede ser simplemente:

{ "method": "sum", "params": [ 20, 22 ], "jsonrpc": "2.0", "id": 1 }

Y la respuesta:

{ "result": 42, "jsonrpc": "2.0", "id": 1 }

Eso es prácticamente todo el mecanismo fundamental.

El id permite además que varias operaciones estén simultáneamente en vuelo. Si enviamos las peticiones 1, 2 y 3, las respuestas no tienen necesariamente que llegar en ese orden: cada respuesta contiene el identificador que permite relacionarla con su llamada original.

Esta idea no es nueva —Sun RPC ya utilizaba un xid para correlacionar llamadas y respuestas—, pero JSON-RPC consigue expresarla utilizando únicamente unos cuantos campos JSON.

JSON-RPC no es HTTP

Este es probablemente uno de los aspectos más importantes del protocolo.

JSON-RPC no define cómo deben viajar los mensajes.

JSON-RPC define su estructura y las reglas para interpretarlos.

La propia especificación 2.0 dice explícitamente que es transport agnostic: puede utilizarse sobre sockets, HTTP o prácticamente cualquier otro sistema de intercambio de mensajes.

Por ejemplo, podemos utilizar JSON-RPC mediante HTTP:

Client │ │ HTTP POST │ {"jsonrpc":"2.0", ...} ▼ Server

Pero también sobre una conexión TCP persistente:

Client ◄════════ TCP ════════► Server

sobre WebSocket:

Client ◄════ WebSocket ════► Server

o incluso entre dos procesos utilizando stdin y stdout:

Process A │ │ stdout → stdin ▼ Process B

El protocolo solamente se preocupa de los mensajes.

Esta separación resulta especialmente poderosa: el protocolo RPC y el transporte son dos capas diferentes.

Es exactamente la razón por la que podemos encontrarnos JSON-RPC en contextos tan distintos.

Por ejemplo, Geth —una implementación de Ethereum— expone JSON-RPC mediante HTTP, WebSocket y Unix Domain Sockets.

Requests, responses y notifications

JSON-RPC 2.0 define tres formas fundamentales de comunicación.

Una request solicita que el receptor ejecute un método:

{ "jsonrpc": "2.0", "id": 123, "method": "user.get", "params": { "id": 42 } }

El servidor responde utilizando exactamente el mismo id:

{ "jsonrpc": "2.0", "id": 123, "result": { "name": "Alice" } }

Si la operación falla, result desaparece y aparece error:

{ "jsonrpc": "2.0", "id": 123, "error": { "code": -32601, "message": "Method not found" } }

Una respuesta no puede contener simultáneamente result y error.

Pero JSON-RPC también permite enviar mensajes para los que no esperamos ninguna respuesta.

Son las notifications.

La diferencia es sorprendentemente pequeña: simplemente no contienen id.

{ "jsonrpc": "2.0", "method": "log", "params": { "message": "Server started" } }

Al no existir identificador que correlacionar, el receptor no debe enviar ninguna respuesta.

Esta combinación de llamadas y notificaciones resulta muy útil para protocolos bidireccionales y orientados a eventos.

Parámetros por posición o por nombre

JSON-RPC 2.0 permite además dos maneras de enviar argumentos.

Por posición:

{ "jsonrpc": "2.0", "method": "subtract", "params": [ 42, 23 ], "id": 1 }

o por nombre:

{ "jsonrpc": "2.0", "method": "subtract", "params": { "minuend": 42, "subtrahend": 23 }, "id": 1 }

La primera recuerda a:

subtract(42, 23)

y la segunda conceptualmente a:

subtract(minuend=42, subtrahend=23)

La especificación permite ambas formas.

JSON-RPC 1.0

Aunque el proyecto comenzó en marzo de 2004, la especificación JSON-RPC 1.0 que se conserva oficialmente está fechada en 2005.

Su estructura fundamental ya era muy reconocible:

{ "method": "echo", "params": [ "Hello" ], "id": 1 }

Y una respuesta:

{ "result": "Hello", "error": null, "id": 1 }

Pero existían varias ambigüedades y decisiones que con el tiempo se consideraron mejorables.

La comunidad empezó entonces a discutir posibles evoluciones del protocolo.

Hubo incluso un JSON-RPC 1.1 Working Draft, considerablemente más complejo y más ligado a HTTP. Finalmente esa dirección fue abandonada. Matt Morley, participante en el diseño posterior, recordaría años después que parte del trabajo que condujo a 2.0 consistió precisamente en eliminar complejidad e implicaciones introducidas por aquella propuesta 1.1.

Es una historia interesante porque la evolución del protocolo no siguió la típica dirección de añadir cada vez más funcionalidades.

En buena medida ocurrió lo contrario.

JSON-RPC 2.0: volver a hacer pequeño el núcleo

La versión actual, JSON-RPC 2.0, tiene como fecha de origen oficial el 26 de marzo de 2010, basada a su vez en una versión del 24 de mayo de 2009.

La autoría ya no aparece atribuida a una única persona, sino al JSON-RPC Working Group. La especificación fue posteriormente actualizada el 4 de enero de 2013.

Uno de sus cambios visibles es el campo:

"jsonrpc": "2.0"

que permite distinguir inequívocamente un mensaje 2.0 de uno 1.0.

También formalizó mejor las notificaciones, los errores, los parámetros por nombre y las llamadas batch.

Por ejemplo, varias llamadas pueden enviarse juntas:

[ { "jsonrpc": "2.0", "method": "sum", "params": [ 1, 2 ], "id": 1 }, { "jsonrpc": "2.0", "method": "sum", "params": [ 10, 20 ], "id": 2 } ]

y producir:

[ { "jsonrpc": "2.0", "result": 3, "id": 1 }, { "jsonrpc": "2.0", "result": 30, "id": 2 } ]

Pero quizá la decisión de diseño más importante de 2.0 fue precisamente no especificar demasiadas cosas.

Matt Morley explicó en una discusión histórica del grupo que uno de los objetivos era conservar un núcleo mínimo y utilizar especificaciones de extensión para funcionalidades adicionales. Incluso cuestiones relativas al transporte quedaron deliberadamente fuera del protocolo.

Esta filosofía explica buena parte de su longevidad.

Entonces, ¿JSON-RPC es un RFC?

No.

Y esta distinción es importante.

JSON sí ha sido formalizado posteriormente mediante estándares como ECMA-404 y diferentes RFC del IETF.

JSON-RPC, en cambio, no es un RFC del IETF.

Es una especificación independiente mantenida históricamente por su comunidad y por el JSON-RPC Working Group.

Eso no impide que se haya convertido en la base de protocolos extremadamente importantes.

De hecho, su pequeño tamaño hace que sea especialmente atractivo como capa sobre la que construir protocolos más específicos.

JSON-RPC no intenta definir qué significa:

textDocument/hover

o:

tools/call

Solamente define cómo representar la llamada y cómo correlacionar su respuesta.

El protocolo superior decide el resto.

JSON-RPC y Language Server Protocol

Uno de sus usos modernos más interesantes es Language Server Protocol, desarrollado originalmente por Microsoft.

Antes de LSP, implementar soporte para un lenguaje en diferentes editores implicaba repetir una enorme cantidad de trabajo.

Si queríamos autocompletado, navegación a definiciones o información contextual de Go, Python o TypeScript, cada editor necesitaba integraciones específicas.

LSP separó esas dos cosas.

┌──────────┐ ┌─────────────────┐ │ Editor │ ◄── JSON-RPC ─► │ Language Server │ └──────────┘ └─────────────────┘

Visual Studio Code, Vim, Emacs u otro editor pueden hablar con un servidor especializado en un lenguaje mediante un protocolo común.

El transporte añade su propio framing, pero el contenido de las peticiones, respuestas y notificaciones utiliza JSON-RPC 2.0.

Por ejemplo:

{ "jsonrpc": "2.0", "id": 42, "method": "textDocument/completion", "params": { "...": "..." } }

JSON-RPC no necesita saber qué significa textDocument/completion.

LSP lo define encima.

JSON-RPC y Model Context Protocol

Más de veinte años después de aquella conversación de IRC de 2004 aparece otro ejemplo especialmente interesante: Model Context Protocol, MCP.

MCP define cómo aplicaciones de inteligencia artificial pueden comunicarse con servidores que proporcionan herramientas, recursos, prompts y otras capacidades.

Y debajo de esa abstracción encontramos de nuevo lo mismo:

JSON-RPC 2.0.

La especificación de MCP establece explícitamente que los mensajes entre clientes y servidores deben seguir JSON-RPC 2.0.

Por eso una inicialización MCP puede tener este aspecto:

{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "...": "..." } }

Y una llamada a una herramienta:

{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "get_weather", "arguments": { "city": "Madrid" } } }

MCP define qué son initialize, tools/call, resources/read o prompts/get.

JSON-RPC define el mecanismo común que permite solicitarlos y devolver sus resultados.

Es una separación de responsabilidades sorprendentemente limpia:

┌─────────────────────────────┐ │ MCP │ │ tools, resources, prompts… │ ├─────────────────────────────┤ │ JSON-RPC 2.0 │ │ request / response / notif. │ ├─────────────────────────────┤ │ stdio / HTTP / ... │ │ transporte │ └─────────────────────────────┘

JSON-RPC no es, por tanto, “parte de JSON”.

Tampoco es simplemente una forma concreta de hacer HTTP.

Es otro protocolo, construido utilizando JSON como formato de mensajes y sobre el que a su vez pueden construirse protocolos de más alto nivel.

Otros lugares donde sigue apareciendo

Ethereum es otro ejemplo importante.

La interfaz canónica utilizada por aplicaciones y herramientas para comunicarse con los clientes de ejecución de Ethereum es una API JSON-RPC. Métodos conocidos como:

eth_getBalance eth_call eth_sendRawTransaction

son operaciones RPC disponibles mediante esa interfaz.

JSON-RPC funciona particularmente bien en este tipo de sistemas porque proporciona una estructura de comunicación sin imponer el modelo de recursos característico de una API REST.

En REST normalmente modelamos entidades:

GET /users/42 POST /users DELETE /users/42

JSON-RPC modela operaciones:

user.get user.create user.delete

Ningún modelo es universalmente mejor.

Representan formas diferentes de pensar una API.

Cuando el dominio consiste naturalmente en acciones, comandos o procedimientos, RPC puede resultar extraordinariamente natural.

Lo que JSON-RPC deliberadamente no hace

Quizá la mejor forma de entender JSON-RPC sea observar todo lo que no intenta solucionar.

No especifica discovery de servicios.

No especifica autenticación.

No especifica autorización.

No especifica cómo versionar nuestra API.

No especifica HTTP.

No especifica WebSocket.

No especifica cómo abrir o cerrar conexiones.

No especifica cómo documentar los métodos disponibles.

No especifica los tipos particulares de parámetros de nuestra aplicación.

Para algunos sistemas, esto puede ser una desventaja.

Para otros, es precisamente su mayor virtud.

Cuando se necesita una descripción formal de una API JSON-RPC existe, por ejemplo, OpenRPC, una especificación inspirada conceptualmente en herramientas como OpenAPI pero diseñada para describir APIs JSON-RPC 2.0.

Esto permite mantener pequeño el protocolo base y añadir otras capas solamente cuando son necesarias.

Veinte años haciendo casi lo mismo

Probablemente lo más llamativo de JSON-RPC sea comparar una llamada moderna con las ideas originales del protocolo.

En 2004 el objetivo era:

método + parámetros + identificador

En 2026 seguimos enviando:

{ "jsonrpc": "2.0", "method": "...", "params": {}, "id": 1 }

En una industria que tiende a acumular capas, abstracciones y nuevos estándares, JSON-RPC sobrevivió precisamente gracias a la estrategia contraria.

No intenta ser un framework.

No intenta ser un sistema completo de web services.

Ni siquiera intenta decidir cómo mandar sus propios mensajes por la red.

Simplemente define una forma común de decir:

quiero ejecutar esto, con estos argumentos, esta llamada tiene este identificador, y esta es su respuesta.

Sun RPC ya perseguía esa idea en los años ochenta.

XML-RPC la llevó a la Web en los noventa.

JSON-RPC eliminó casi todo lo que sobraba en 2004.

Y dos décadas después, editores de código, nodos de blockchain y agentes de inteligencia artificial siguen hablando mediante la misma pequeña abstracción.

Quizá precisamente porque, desde el principio, fue diseñado para ser simple.

Fuentes y lecturas recomendadas