MCP并不局限于Claude桌面,它可以用任何支持它的其他LLM客户端使用。考虑到这一点,我们决定构建一个MCP CLI客户端来展示这一点。这个MCP客户端可以更快速地测试MCP服务器。
模型上下文协议(MCP)在AI领域持续获得关注,自从Neon MCP服务器发布以来(大约两周前),社区已经在广泛的领域内构建了数十个这些服务器[链接]。然而,Claude桌面应用程序已经成为默认的MCP客户端,大多数服务器都有专门的说明来指导如何与该客户端集成。
但MCP并不局限于Claude桌面,它可以用任何支持它的其他LLM客户端使用。考虑到这一点,我们决定构建一个MCP CLI客户端来展示这一点。这个MCP客户端可以更快速地测试MCP服务器。
所有MCP客户端都基于相同的原理并遵循相同的协议。对于工具使用(我们的用例),需要实现的主要概念如下:
第一步是连接到MCP服务器,以便它可以发现和使用服务器上的工具。
const mcpClient = new Client(
{ name: 'cli-client', version: '1.0.0' },
{ capabilities: {} },
);
// 此函数将MCP客户端连接到MCP服务器
await mcpClient.connect(new StdioClientTransport(serverConfig));
我们需要从MCP服务器获取可用工具。这允许LLM知道在交互过程中可以使用哪些工具
// 此函数将返回MCP服务器上可用工具的列表
const toolsAvailable = await this.mcpClient.request(
{ method: 'tools/list' },
ListToolsResultSchema,
);
一旦LLM决定使用哪个工具,我们需要调用MCP服务器上的工具处理程序。
// 此函数将在MCP服务器上调用工具处理程序
const toolResult = await this.mcpClient.request(
{
method: 'tools/call',
params: {
name: toolName,
arguments: toolArgs,
},
},
CallToolResultSchema,
);
这是一个多步骤的过程,将LLM连接到可用工具:
由于我们正在使用来自Anthropic API的工具API,如果仅仅依赖他们的官方SDK,则会简单得多。
// 1- 发送初始提示
const response = await this.anthropicClient.messages.create({
messages: [
{
role: 'user',
content: 'Can you list my Neon projects?',
},
],
model: 'claude-3-5-sonnet-20241022',
max_tokens: 8192,
tools: this.tools,
});
for (const content of response.content) {
// 2- 等待LLM响应工具使用
if (content.type === 'tool_use') {
const toolName = content.name;
const toolArgs = content.input;
// 3- 在MCP服务器上调用工具处理程序
const toolResult = await this.mcpClient.request(
{
method: 'tools/call',
params: {
name: toolName,
arguments: toolArgs,
},
},
CallToolResultSchema,
);
// 4- 将工具结果注入LLM的上下文中
const contextWithToolResult = [
...previousMessages,
{ role: 'user', content: toolResult.content },
];
// 5- 向LLM发送下一个提示
const nextResponse = await this.anthropicClient.messages.create({
messages: contextWithToolResult,
model: 'claude-3-5-sonnet-20241022',
max_tokens: 8192,
});
}
}
一旦我们有了所有核心组件,我们只需要构建一个酷炫的CLI客户端,可以用来与MCP服务器交互。
处理LLM消息和工具使用。重要的是我们要在每次交互之间保留消息,以便可以将工具结果注入LLM的上下文中。
private async processQuery(query: string) {
try {
// 1 - 向LLM发送用户的查询
this.messages.push({ role: 'user', content: query });
const response = await this.anthropicClient.messages.create({
messages: this.messages,
model: 'claude-3-5-sonnet-20241022',
tools: this.tools,
});
// 2 - 处理LLM响应
for (const content of response.content) {
if (content.type === 'text') {
process.stdout.write(content.text);
}
// 3 - 处理工具使用
if (content.type === 'tool_use') {
const toolResult = await this.mcpClient.request({
method: 'tools/call',
params: {
name: content.name,
arguments: content.input,
}
});
// 4 - 将工具结果添加到对话中
this.messages.push({
role: 'user',
content: JSON.stringify(toolResult)
});
// 5 - 获取LLM对工具结果的响应
const nextResponse = await this.anthropicClient.messages.create({
messages: this.messages,
model: 'claude-3-5-sonnet-20241022'
});
// 6 - 显示LLM的响应
if (nextResponse.content[0].type === 'text') {
process.stdout.write(nextResponse.content[0].text);
}
}
}
} catch (error) {
console.error('Error during query processing:', error);
}
}
创建一个聊天循环,用于向LLM发送消息并处理响应。
private async chat_loop() {
while (true) {
try {
const query = (await this.rl.question(styles.prompt)).trim();
// 处理查询
await this.processQuery(query);
} catch (error) {
console.error(styles.error('\\\\nError:'), error);
}
}
}
设置客户端的主入口点,初始化MCP客户端,获取工具并启动聊天循环
// 这是客户端的主入口点
async start() {
try {
console.log(styles.info('🤖 Interactive Claude CLI'));
console.log(
styles.info(`Type your queries or "${EXIT_COMMAND}" to exit`),
);
// 1 - 将MCP客户端连接到MCP服务器
await this.mcpClient.connect(this.transport);
// 2 - 获取MCP服务器上可用的工具
await this.initMCPTools();
// 3 - 启动聊天循环
await this.chat_loop();
} catch (error) {
console.error(styles.error('Failed to initialize tools:'), error);
process.exit(1);
} finally {
this.rl.close();
process.exit(0);
}
}
现在我们已经构建了一个通用的MCP客户端,可以通过传递MCP服务器URL和其他所需参数来运行它。
const cli = new InteractiveCLI({
command: '../dist/index.js',
args: ['start', process.env.NEON_API_KEY!],
});
cli.start();
这个简单实现有两个主要缺点:
幸运的是,这两个问题已经在我们在Neon构建的MCP客户端CLI中得到了解决。
参考:https://neon.tech/blog/building-a-cli-client-for-model-context-protocol-servers
最后,推荐大家关注一下开源项目:LangChat,Java生态下的AIGC大模型产品解决方案。