MCP 协议入门:JSON RPC 的实现
根据 JSON RPC 的规范,一个完整的 JSON RPC 实现可以分为三个核心步骤:
- 服务端注册:服务端维护一个方法注册表,将可调用的函数名和对应的处理函数进行映射
- 客户端发现:客户端在启动时通过特定的方法(如
listMethods)从服务端拉取所有已注册的函数列表 - 动态代理:客户端根据拉取到的函数列表,动态创建对应的代理函数,使得可以像调用本地函数一样调用远程函数
这种设计实现了服务端和客户端的解耦,客户端无需预先知道服务端提供了哪些函数,而是通过运行时发现机制动态获取。基本流程的代码如下:
// 服务端
class Server {
// 用于函数的注册
methods = new Map();
// 注册函数
registry(name, handler) {
this.methods.set(name, handler);
}
// 处理 JSON RPC 请求
async handleRequest(request) {
const { method, params, id } = request;
if (!this.methods.has(method)) {
return {
jsonrpc: "2.0",
error: { code: -32601, message: "Method not found" },
id,
};
}
try {
const handler = this.methods.get(method);
const result = await handler(...params);
return {
jsonrpc: "2.0",
result,
id,
};
} catch (error) {
return {
jsonrpc: "2.0",
error: { code: -32603, message: error.message },
id,
};
}
}
}
const server = new Server();
server.registry("functioncall", async (param1, param2) => {
// 函数实现
return { result: `处理 ${param1} 和 ${param2}` };
});
// 客户端
class Client {
// 构造函数,接收服务端地址
constructor(url) {
this.url = url;
this.methods = new Set(); // 存储已拉取的函数名
}
// 获取注册函数
async pull() {
// 调用服务端的 listMethods 方法获取所有注册的函数
const response = await this.send("listMethods", []);
if (response.result) {
// 将拉取到的函数名注册到客户端
response.result.forEach((methodName) => {
this.registry(methodName);
});
}
}
// 动态注册函数
registry(methodName) {
if (this.methods.has(methodName)) {
return; // 已注册,跳过
}
this.methods.add(methodName);
// 动态创建可调用函数
this[methodName] = async (...params) => {
return await this.send(methodName, params);
};
}
// 包裹函数
// 用于发送请求
async send(method, params) {
const request = {
jsonrpc: "2.0",
method,
params,
id: Date.now() + Math.random(), // 生成唯一 ID
};
const response = await fetch(this.url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
});
const data = await response.json();
if (data.error) {
throw new Error(data.error.message);
}
return data;
}
}
const client = new Client("http://localhost:3000/rpc");
// 启动时拉取远程注册的函数
await client.pull();
// 动态调用函数
const result = await client.functioncall("param1", "param2");