前言
了解Model Context Protocol(MCP),首先需要了解jsonrpc2.0协议。MCP使用jsonrpc2.0协议实现了MCPServer和MCPClient两者交互的数据结构。
JSONRPC2.0
在SpringAI MCP技术试用文中说过,MCP只是一个standardized(标准化的;规范化的)协议,而MCP协议的核心是jsonrpc2.0,具体可以参考官方文档和MCP传输协议文档。
为什么用jsonrpc2.0?
0)生态完善:JSON-RPC 2.0 有丰富的库和工具支持。
1)和开发语言无关:基本上所有开发语言都支持json协议。
2)简单实用:结构简单易读性好。
3)轻量级:能大大减少数据传输,减少AI数据交互过程中的延迟。
MCP中jsonrpc2.0的定义
下文主要以mcp tools原语为例。
initialize
# 请求
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {}
},
"clientInfo": {
"name": "example-client",
"version": "1.0.0"
}
}
}
# 响应
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {
"listChanged": true
},
"resources": {}
},
"serverInfo": {
"name": "example-server",
"version": "1.0.0"
}
}
}
initialized
# 请求
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
# 响应
无
tools/list
# 请求
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}
# 响应
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "com.example.calculator/arithmetic",
"title": "Calculator",
"description": "Perform mathematical calculations including basic arithmetic, trigonometric functions, and algebraic operations",
"inputSchema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "Mathematical expression to evaluate (e.g., '2 + 3 * 4', 'sin(30)', 'sqrt(16)')"
}
},
"required": ["expression"]
}
}
]
}
}
tools/call
# 请求
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "com.example.weather/current",
"arguments": {
"location": "San Francisco",
"units": "imperial"
}
}
}
# 响应
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "Current weather in San Francisco: 68°F, partly cloudy with light winds from the west at 8 mph. Humidity: 65%"
}
]
}
}
一些说明
1)MCP官方文档写的非常清楚,可以参考官方文档中的Architecture Overview->Data Layer 模块。
2)官方文档主要以一个地区天气查询MCP为例,说明了MCP从连接建立到tool调用的过程中数据以jsonrpc2.0协议传输的数据结构,可以看到非常的简单明了,易于理解和实现。
Demo
这里使用java spring boot简单模拟mcp tool/call的调用过程。
# JsonRpcServerController.java
import cn.chinatelecom.dw.user.app.utils.JSONUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: wanghaoguang
* @CreateTime: 2025/7/28 15:22
*/
@RestController
public class JsonRpcServerController {
private final static Logger LOG = LoggerFactory
.getLogger(JsonRpcServerController.class);
@PostMapping(value = "/jsonrpc", consumes = MediaType.APPLICATION_JSON_VALUE, produces =
MediaType.APPLICATION_JSON_VALUE)
private ResponseEntity<String> jsonrpc(@RequestBody String requestBody) {
try {
ObjectNode objNode = JSONUtils.parseObject(requestBody);
String methodName = objNode.get("method").asText();
JsonNode params = objNode.get("params");
if (params == null) {
params = JSONUtils.createObjectNode();
}
Integer requestId = objNode.get("id").asInt();
LOG.info("jsonrpc_endpoint params {}", objNode);
ObjectNode response = JSONUtils.createObjectNode();
if ("calculate".equals(methodName)) {
try {
String expression = params.get("expression").asText();
double result = safeCalculate(expression);
response.put("result", result);
} catch (Exception e) {
int code = -32603;
String message = e.getMessage();
response = buildErrObjectNode(requestId, code, message);
}
} else {
int code = -32601;
String message = "Method not found";
buildErrObjectNode(requestId, code, message);
}
return ResponseEntity.ok(JSONUtils.toJsonString(response));
} catch (Exception e) {
LOG.info("jsonrpc_endpoint Error: {}", e.getMessage());
int code = -32000;
String message = "Internal server error " + e.getMessage();
return ResponseEntity.status(500).body(JSONUtils.toJsonString(buildErrObjectNode(0, code, message)));
}
}
private ObjectNode buildErrObjectNode(int requestId, int code, String message) {
ObjectNode response = JSONUtils.createObjectNode();
response.put("id", requestId);
response.put("jsonrpc", "2.0");
ObjectNode error = JSONUtils.createObjectNode();
error.put("code", code);
error.put("message", message);
response.set("error", error);
return response;
}
private double safeCalculate(String expression) {
try {
return Double.parseDouble(String.valueOf(eval(expression)));
} catch (Exception e) {
throw new IllegalArgumentException("Invalid expression");
}
}
private Object eval(String expression) {
try {
return new javax.script.ScriptEngineManager().getEngineByName("JavaScript").eval(expression);
} catch (Exception e) {
throw new IllegalArgumentException("Invalid expression");
}
}
}
# JsonRpcClient
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
* @Author: wanghaoguang
* @CreateTime: 2025/7/28 15:35
*/
public class JsonRpcClient {
private final static Logger LOG = LoggerFactory
.getLogger(JsonRpcClient.class);
private final String serverUrl;
private int requestId = 1;
public JsonRpcClient(String serverUrl) {
this.serverUrl = serverUrl;
}
public JsonNode call(String method, JsonNode params, int timeout) {
ObjectNode payload = JSONUtils.createObjectNode();
payload.put("jsonrpc", "2.0");
payload.put("method", method);
if (params == null) {
params = JSONUtils.createObjectNode();
}
payload.set("params", params);
payload.put("id", requestId++);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(timeout, java.util.concurrent.TimeUnit.SECONDS)
.readTimeout(timeout, java.util.concurrent.TimeUnit.SECONDS)
.build();
MediaType JSON = MediaType.get("application/json; charset=utf-8");
try {
RequestBody body = RequestBody.create(JSONUtils.toJsonString(payload), JSON);
Request request = new Request.Builder()
.url(serverUrl)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
String responseBody = response.body().string();
return JSONUtils.parseObject(responseBody);
} else {
LOG.info("Request failed with code: {}", response.code());
}
}
} catch (IOException e) {
LOG.info("Error occurred while making the request: {}", e.getMessage());
}
return null;
}
public static void main(String[] args) {
JsonRpcClient client = new JsonRpcClient("http://127.0.0.1:9022/jsonrpc");
ObjectNode params = JSONUtils.createObjectNode();
params.put("expression", "3+5");
JsonNode result = client.call("calculate", params, 10);
System.out.println(result);
}
}
一些说明:
1)可以看到使用java启动一个http服务,基于jsonrpc2.0协议可以模拟mcp协议的交互流程。
2)上文主要提供java版本实现,python可以自己实现试试。
总结
1,MCP使用jsonrpc定义了MCPServer和MCPClient之间交互的数据结构,不管是stdio、sse、streamable-http、sampling那种协议。
2,jsonrpc协议和语言无关,方便简单易用,使用最简单的json包处理即可,自己也可以用自己擅长的开发语言按MCP定义的交互协议编写自己的MCPServer。