admin管理员组文章数量:1029777
MCP SDK 快速接入 DeepSeek 并添加工具!万万没想到MCP这么简单好用!
前言
重新整理了上篇文章,主要修正了错误的地方,加上了正确的截图和代码!感谢大家的积极指正!
这篇文章记录一下我用 MCPTypeScriptSDK
实现一个自包含的 AI
聊天应用的过程:内部包含 MCP
服务器提供上下文,客户端拿上下文再去调 LLM
接口拿回答!
正文
MCP
是什么?
简单说,MCP
是一个给 AI
应用提供上下文的标准协议。你可以把它理解成一个服务标准,它规定了“资源”和“工具”的接口规范,然后通过客户端连接这些接口,就可以组合出丰富的上下文数据。比如说资源可以是“当前时间”、“用户历史记录”,工具可以是“数据库搜索”、“调用外部 API
”。
它采用的是客户端-服务器架构,Server
暴露上下文能力,Client
拉取这些上下文,再拿去调语言模型生成回答,而 Transport
负责 Server
和 Client
的通信部分!
MCP 架构
(AI 帮我画的图)
其中图片中的 Transport
层还分为:
StdioServerTransport
:用于CLI
工具对接stdin/stdout
SSEServerTransport
:用于HTTP
通信StdioClientTransport
:客户端以子进程方式拉起服务端,这个不常用
另外,Server
层分为:
Server
基本类:原始的类,适合自己定制功能!McpServer
基于Server
封装好了可以快速使用的方法!
❝注意:基本类和封装类的接口有很大不同,具体请参看
README
文件!
安装依赖
用的是官方的 TypeScriptSDK
:
仓库:
官网:
代码语言:javascript代码运行次数:0运行复制npm install @modelcontextprotocol/sdk axios
DeepSeek
没有官方 SDK
,用的是 HTTP API
,所以需要 axios
!
记得把 API Key
放到 .env
或直接配置成环境变量,我用的 DEEPSEEK_API_KEY
。
实现一个 McpServer
我们先实现一个本地 McpServer
,实现两个东西:
- 当前时间(资源)
- 本地“知识库”搜索(工具)
代码如下:
代码语言:javascript代码运行次数:0运行复制// src/server.js
import {
McpServer,
ResourceTemplate,
} from"@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from"@modelcontextprotocol/sdk/server/stdio.js";
import { z } from"zod";
const facts = [
"公理1: 生存是文明的第一需要.",
"公理2: 文明不断增长和扩张,但宇宙中的物质总量保持不变.",
].map((f) => f.toLowerCase());
try {
const server = new McpServer({
name: "mcp-cli-server",
version: "1.0.0",
});
// 使用 Zod 定义工具的输入模式
server.tool(
"search_local_database",
{
query: z.string(),
},
async ({ query }) => {
console.log("Tool called with query:", query);
const queryTerms = query.toLowerCase().split(/\s+/);
const results = facts.filter((fact) =>
queryTerms.some((term) => fact.includes(term))
);
return {
content: [
{
type: "text",
text: results.length === 0 ? "未找到相关公理" : results.join("\n"),
},
],
};
}
);
// 定义资源
server.resource(
"current_time",
new ResourceTemplate("time://current", { list: undefined }),
async (uri) => ({
contents: [{ uri: uri.href, text: newDate().toLocaleString() }],
})
);
await server.connect(new StdioServerTransport());
console.log("Server is running...");
} catch (err) {
console.error("Server connection failed:", err);
}
这样一来,我们的服务端就能通过 MCP 协议对外暴露两个上下文能力了。
配置 MCP Client
MCP 的客户端用来连接服务器并获取资源或调用工具:
代码语言:javascript代码运行次数:0运行复制// src/client.js;
import { Client } from"@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from"@modelcontextprotocol/sdk/client/stdio.js";
exportasyncfunction createClient() {
const client = new Client({
name: "Demo",
version: "1.0.0",
});
const transport = new StdioClientTransport({
command: "node",
args: ["src/server.js"],
});
try {
await client.connect(transport);
console.log("Client connected successfully");
} catch (err) {
console.error("Client connection failed:", err);
throw err;
}
// 可选:添加客户端方法调用后的调试
return client;
}
连上之后,我们就可以开始调用服务端的资源和工具了。
获取上下文
我们设定一个简单的逻辑:每次用户提问,客户端都会获取当前时间;如果问题里包含 公理
,那就调用搜索工具查一下本地知识库:
async function getContext(client, question) {
let currentTime = "";
let additionalContext = "";
try {
const resources = await client.readResource(
{ uri: "time://current" },
{ timeout: 15000 }
); // 增加超时时间
console.log("Resources response:", resources);
currentTime = resources.contents[0]?.text ||
newDate().toLocaleString(); // 注意:resources 直接包含 contents
} catch (err) {
console.error("Resource read error:", err);
currentTime = newDate().toLocaleString();
}
if (question.toLowerCase().includes("公理")) {
console.log("Searching for axioms...", question);
try {
const result = await client.getPrompt({
name: "search_local_database",
arguments: { query: question },
});
console.log("Tool result:", result);
additionalContext = result?.[0]?.text || "No results found.";
} catch (err) {
console.error("Tool call error:", err);
additionalContext = "Error searching database.";
}
}
return { currentTime, additionalContext };
}
集成 DeepSeek,开始问答
DeepSeek
使用的是标准 OpenAI
接口风格,HTTP POST
请求即可。这里我们用 axios
调用:
import axios from"axios";
asyncfunction askLLM(prompt) {
try {
console.log("Calling LLM with prompt:", prompt);
const res = await axios.post(
";,
{
model: "deepseek-chat",
messages: [{ role: "user", content: prompt }],
max_tokens: 2048,
stream: false,
temperature: 0.7,
},
{
headers: {
Authorization: `Bearer ${process.env.DEEPSEEK_API_KEY}`,
"Content-Type": "application/json",
},
timeout: 1000000,
}
);
console.log("LLM response:", res.data);
return res.data.choices[0].message.content;
} catch (err) {
console.error("LLM error:", err);
return"Error calling LLM.";
}
}
完整的代码,包含用命令行做一个简单的交互界面:
代码语言:javascript代码运行次数:0运行复制// src/index.js
import readline from"readline";
import axios from"axios";
import { createClient } from"./client.js";
import { DEEPSEEK_API_KEY } from"./config.js";
asyncfunction askLLM(prompt) {
try {
console.log("Calling LLM with prompt:", prompt);
const res = await axios.post(
";,
{
model: "deepseek-chat",
messages: [{ role: "user", content: prompt }],
max_tokens: 2048,
stream: false,
temperature: 0.7,
},
{
headers: {
Authorization: `Bearer ${DEEPSEEK_API_KEY}`,
"Content-Type": "application/json",
},
timeout: 1000000,
}
);
return res.data.choices[0].message.content;
} catch (err) {
console.error("LLM error:", err);
return"Error calling LLM.";
}
}
asyncfunction getContext(client, question) {
let currentTime = "";
let additionalContext = "";
try {
const resources = await client.readResource(
{ uri: "time://current" },
{ timeout: 15000 }
); // 增加超时时间
currentTime = resources.contents[0]?.text || newDate().toLocaleString(); // 注意:resources 直接包含 contents
} catch (err) {
console.error("Resource read error:", err);
currentTime = newDate().toLocaleString();
}
if (question.toLowerCase().includes("公理")) {
try {
// const result = await client.getPrompt({
// name: "search_local_database",
// arguments: { query: question },
// });
const toolResult = await client.callTool({
name: "search_local_database",
arguments: { query: question },
});
console.log("Tool result:", toolResult);
additionalContext = toolResult?.content?.[0]?.text || "No results found.";
} catch (err) {
console.error("Tool call error:", err);
additionalContext = "Error searching database.";
}
}
return { currentTime, additionalContext };
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const client = await createClient();
while (true) {
const question = awaitnewPromise((resolve) =>
rl.question("You: ", resolve)
);
if (question.toLowerCase() === "exit") {
console.log("Exiting...");
rl.close();
process.exit(0);
}
// 使用上下文
const context = await getContext(client, question);
// 不使用上下文
// const context = {};
const prompt = `Time: ${context.currentTime}\nContext: ${context.additionalContext}\nQ: ${question}\nA:`;
console.log("Prompt:", prompt);
const answer = await askLLM(prompt);
console.log('Assistant:', answer);
}
接着在终端运行:
代码语言:javascript代码运行次数:0运行复制# 启动服务器
node src/server.js
代码语言:javascript代码运行次数:0运行复制# 启动客户端
node src/index.js
运行结果:可以看到识别到关键字之后,答案更加集中在特定领域!
未命中关键词
命中了关键词
一些注意点
这个项目虽然小,但也踩了些坑,顺便分享几点:
- MCP SDK 的 server 和 client 都是异步启动的,别忘了加上
await connect()
。 - 工具的入参和 schema 必须严格匹配,否则会抛错。
下面是我的目录结构,做个参考吧!
代码语言:javascript代码运行次数:0运行复制mcp-mini/
├── package.json
├── src/
│ ├── client.js
│ ├── server.js
│ └── index.js
最后
总的来说,MCP TypeScriptSDK
用起来还是挺顺的,适合做一些轻量、模块化、支持上下文的 AI
应用。这种服务 + 客户端 + LLM
的组合模式挺适合本地测试,也方便后续扩展别的服务。
今天的分享就到这了,如果文章中有啥错误,欢迎指正!
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-04-18,如有侵权请联系 cloudcommunity@tencent 删除sdk工具接口客户端DeepSeekMCP SDK 快速接入 DeepSeek 并添加工具!万万没想到MCP这么简单好用!
前言
重新整理了上篇文章,主要修正了错误的地方,加上了正确的截图和代码!感谢大家的积极指正!
这篇文章记录一下我用 MCPTypeScriptSDK
实现一个自包含的 AI
聊天应用的过程:内部包含 MCP
服务器提供上下文,客户端拿上下文再去调 LLM
接口拿回答!
正文
MCP
是什么?
简单说,MCP
是一个给 AI
应用提供上下文的标准协议。你可以把它理解成一个服务标准,它规定了“资源”和“工具”的接口规范,然后通过客户端连接这些接口,就可以组合出丰富的上下文数据。比如说资源可以是“当前时间”、“用户历史记录”,工具可以是“数据库搜索”、“调用外部 API
”。
它采用的是客户端-服务器架构,Server
暴露上下文能力,Client
拉取这些上下文,再拿去调语言模型生成回答,而 Transport
负责 Server
和 Client
的通信部分!
MCP 架构
(AI 帮我画的图)
其中图片中的 Transport
层还分为:
StdioServerTransport
:用于CLI
工具对接stdin/stdout
SSEServerTransport
:用于HTTP
通信StdioClientTransport
:客户端以子进程方式拉起服务端,这个不常用
另外,Server
层分为:
Server
基本类:原始的类,适合自己定制功能!McpServer
基于Server
封装好了可以快速使用的方法!
❝注意:基本类和封装类的接口有很大不同,具体请参看
README
文件!
安装依赖
用的是官方的 TypeScriptSDK
:
仓库:
官网:
代码语言:javascript代码运行次数:0运行复制npm install @modelcontextprotocol/sdk axios
DeepSeek
没有官方 SDK
,用的是 HTTP API
,所以需要 axios
!
记得把 API Key
放到 .env
或直接配置成环境变量,我用的 DEEPSEEK_API_KEY
。
实现一个 McpServer
我们先实现一个本地 McpServer
,实现两个东西:
- 当前时间(资源)
- 本地“知识库”搜索(工具)
代码如下:
代码语言:javascript代码运行次数:0运行复制// src/server.js
import {
McpServer,
ResourceTemplate,
} from"@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from"@modelcontextprotocol/sdk/server/stdio.js";
import { z } from"zod";
const facts = [
"公理1: 生存是文明的第一需要.",
"公理2: 文明不断增长和扩张,但宇宙中的物质总量保持不变.",
].map((f) => f.toLowerCase());
try {
const server = new McpServer({
name: "mcp-cli-server",
version: "1.0.0",
});
// 使用 Zod 定义工具的输入模式
server.tool(
"search_local_database",
{
query: z.string(),
},
async ({ query }) => {
console.log("Tool called with query:", query);
const queryTerms = query.toLowerCase().split(/\s+/);
const results = facts.filter((fact) =>
queryTerms.some((term) => fact.includes(term))
);
return {
content: [
{
type: "text",
text: results.length === 0 ? "未找到相关公理" : results.join("\n"),
},
],
};
}
);
// 定义资源
server.resource(
"current_time",
new ResourceTemplate("time://current", { list: undefined }),
async (uri) => ({
contents: [{ uri: uri.href, text: newDate().toLocaleString() }],
})
);
await server.connect(new StdioServerTransport());
console.log("Server is running...");
} catch (err) {
console.error("Server connection failed:", err);
}
这样一来,我们的服务端就能通过 MCP 协议对外暴露两个上下文能力了。
配置 MCP Client
MCP 的客户端用来连接服务器并获取资源或调用工具:
代码语言:javascript代码运行次数:0运行复制// src/client.js;
import { Client } from"@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from"@modelcontextprotocol/sdk/client/stdio.js";
exportasyncfunction createClient() {
const client = new Client({
name: "Demo",
version: "1.0.0",
});
const transport = new StdioClientTransport({
command: "node",
args: ["src/server.js"],
});
try {
await client.connect(transport);
console.log("Client connected successfully");
} catch (err) {
console.error("Client connection failed:", err);
throw err;
}
// 可选:添加客户端方法调用后的调试
return client;
}
连上之后,我们就可以开始调用服务端的资源和工具了。
获取上下文
我们设定一个简单的逻辑:每次用户提问,客户端都会获取当前时间;如果问题里包含 公理
,那就调用搜索工具查一下本地知识库:
async function getContext(client, question) {
let currentTime = "";
let additionalContext = "";
try {
const resources = await client.readResource(
{ uri: "time://current" },
{ timeout: 15000 }
); // 增加超时时间
console.log("Resources response:", resources);
currentTime = resources.contents[0]?.text ||
newDate().toLocaleString(); // 注意:resources 直接包含 contents
} catch (err) {
console.error("Resource read error:", err);
currentTime = newDate().toLocaleString();
}
if (question.toLowerCase().includes("公理")) {
console.log("Searching for axioms...", question);
try {
const result = await client.getPrompt({
name: "search_local_database",
arguments: { query: question },
});
console.log("Tool result:", result);
additionalContext = result?.[0]?.text || "No results found.";
} catch (err) {
console.error("Tool call error:", err);
additionalContext = "Error searching database.";
}
}
return { currentTime, additionalContext };
}
集成 DeepSeek,开始问答
DeepSeek
使用的是标准 OpenAI
接口风格,HTTP POST
请求即可。这里我们用 axios
调用:
import axios from"axios";
asyncfunction askLLM(prompt) {
try {
console.log("Calling LLM with prompt:", prompt);
const res = await axios.post(
";,
{
model: "deepseek-chat",
messages: [{ role: "user", content: prompt }],
max_tokens: 2048,
stream: false,
temperature: 0.7,
},
{
headers: {
Authorization: `Bearer ${process.env.DEEPSEEK_API_KEY}`,
"Content-Type": "application/json",
},
timeout: 1000000,
}
);
console.log("LLM response:", res.data);
return res.data.choices[0].message.content;
} catch (err) {
console.error("LLM error:", err);
return"Error calling LLM.";
}
}
完整的代码,包含用命令行做一个简单的交互界面:
代码语言:javascript代码运行次数:0运行复制// src/index.js
import readline from"readline";
import axios from"axios";
import { createClient } from"./client.js";
import { DEEPSEEK_API_KEY } from"./config.js";
asyncfunction askLLM(prompt) {
try {
console.log("Calling LLM with prompt:", prompt);
const res = await axios.post(
";,
{
model: "deepseek-chat",
messages: [{ role: "user", content: prompt }],
max_tokens: 2048,
stream: false,
temperature: 0.7,
},
{
headers: {
Authorization: `Bearer ${DEEPSEEK_API_KEY}`,
"Content-Type": "application/json",
},
timeout: 1000000,
}
);
return res.data.choices[0].message.content;
} catch (err) {
console.error("LLM error:", err);
return"Error calling LLM.";
}
}
asyncfunction getContext(client, question) {
let currentTime = "";
let additionalContext = "";
try {
const resources = await client.readResource(
{ uri: "time://current" },
{ timeout: 15000 }
); // 增加超时时间
currentTime = resources.contents[0]?.text || newDate().toLocaleString(); // 注意:resources 直接包含 contents
} catch (err) {
console.error("Resource read error:", err);
currentTime = newDate().toLocaleString();
}
if (question.toLowerCase().includes("公理")) {
try {
// const result = await client.getPrompt({
// name: "search_local_database",
// arguments: { query: question },
// });
const toolResult = await client.callTool({
name: "search_local_database",
arguments: { query: question },
});
console.log("Tool result:", toolResult);
additionalContext = toolResult?.content?.[0]?.text || "No results found.";
} catch (err) {
console.error("Tool call error:", err);
additionalContext = "Error searching database.";
}
}
return { currentTime, additionalContext };
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const client = await createClient();
while (true) {
const question = awaitnewPromise((resolve) =>
rl.question("You: ", resolve)
);
if (question.toLowerCase() === "exit") {
console.log("Exiting...");
rl.close();
process.exit(0);
}
// 使用上下文
const context = await getContext(client, question);
// 不使用上下文
// const context = {};
const prompt = `Time: ${context.currentTime}\nContext: ${context.additionalContext}\nQ: ${question}\nA:`;
console.log("Prompt:", prompt);
const answer = await askLLM(prompt);
console.log('Assistant:', answer);
}
接着在终端运行:
代码语言:javascript代码运行次数:0运行复制# 启动服务器
node src/server.js
代码语言:javascript代码运行次数:0运行复制# 启动客户端
node src/index.js
运行结果:可以看到识别到关键字之后,答案更加集中在特定领域!
未命中关键词
命中了关键词
一些注意点
这个项目虽然小,但也踩了些坑,顺便分享几点:
- MCP SDK 的 server 和 client 都是异步启动的,别忘了加上
await connect()
。 - 工具的入参和 schema 必须严格匹配,否则会抛错。
下面是我的目录结构,做个参考吧!
代码语言:javascript代码运行次数:0运行复制mcp-mini/
├── package.json
├── src/
│ ├── client.js
│ ├── server.js
│ └── index.js
最后
总的来说,MCP TypeScriptSDK
用起来还是挺顺的,适合做一些轻量、模块化、支持上下文的 AI
应用。这种服务 + 客户端 + LLM
的组合模式挺适合本地测试,也方便后续扩展别的服务。
今天的分享就到这了,如果文章中有啥错误,欢迎指正!
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-04-18,如有侵权请联系 cloudcommunity@tencent 删除sdk工具接口客户端DeepSeek本文标签: MCP SDK 快速接入 DeepSeek 并添加工具!万万没想到MCP这么简单好用!
版权声明:本文标题:MCP SDK 快速接入 DeepSeek 并添加工具!万万没想到MCP这么简单好用! 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/jiaocheng/1747613557a2193391.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论