【MCP-01】JSON-RPC2.0协议

前言

了解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原语为例。


企业微信截图_17540465806557.png

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);
    }
}
企业微信截图_17540469737564.png

企业微信截图_17540470042799.png

一些说明:
1)可以看到使用java启动一个http服务,基于jsonrpc2.0协议可以模拟mcp协议的交互流程。
2)上文主要提供java版本实现,python可以自己实现试试。

总结

1,MCP使用jsonrpc定义了MCPServer和MCPClient之间交互的数据结构,不管是stdio、sse、streamable-http、sampling那种协议。
2,jsonrpc协议和语言无关,方便简单易用,使用最简单的json包处理即可,自己也可以用自己擅长的开发语言按MCP定义的交互协议编写自己的MCPServer。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。