UI + MCP Client + MCP Server(并且鏈接多個Server)

在這里插入圖片描述

項目結構

在這里插入圖片描述
前端項目--------->MCP Client----------->MCP Server
在這里插入圖片描述
server就不過多贅述了,他只是相當于添加了多個的tools

鏈接前后端

http.createServer創建一個服務器


// ----------------------------------------------------------------
// server.js
import http from "http";
import url from "url";
// ----------------------------------------------------------------  const server = http.createServer(async (req: any, res: any) => {const parsedUrl = url.parse(req.url, true); // 解析 url(含 query 參數)const name = parsedUrl.query.name; // ? 獲取 ?name=xxx 的值res.setHeader("Access-Control-Allow-Origin", "*"); // 允許跨域res.setHeader("Content-Type", "application/json; charset=utf-8");// 簡單路由匹配if (req.method === "GET" && parsedUrl.pathname === "/api/hello") {console.log("收到請求參數 name =", name);const ans = await mcpClient.processQuery(name as string);console.log("AI 回復:", ans);res.end(JSON.stringify({ message: ans }));} else {res.statusCode = 404;res.end(JSON.stringify({ error: "接口未找到" }));}});// ----------------------------------------------------------------// 啟動服務器server.listen(3002, () => {console.log("🚀 本地服務器啟動成功:http://127.0.0.1:3002");});

如何區分多個Server

通過獲取配置的server,來進行循環,解構出來所有的tools并且綁定對應的serverName來區分屬于哪個Server

const paths = [{name: "mcp",args: "/Users/v-yangziqi1/Desktop/work/mcpserver/mcp/build/index.js",},{name: "mcp1",args: "/Users/v-yangziqi1/Desktop/work/mcpserver/mcp1/build/index.js",},
];// 初始化mcp server連接async connectToServer(serverScriptPath?: string) {if (!serverScriptPath) return;const res = await Promise.all(paths.map((item, index) => {this.mcpServer.push(new Client({ name: "mcp-client-cli-" + item.name, version: "1.0.0" }));const mcp = this.mcpServer[index]; // 創建 MCP Client 實例// 判斷文件類型const state = item.args.endsWith(".py")? process.platform === "win32"? "python": "python3": process.execPath;//  建立mcp傳輸層const transport = new StdioClientTransport({command: state,args: [item.args],});mcp.connect(transport); // connect 方法用于連接到 MCP Server。// 獲取 MCP Server 上的所有工具名稱return mcp.listTools();}));const nestedArr = res.map(({ tools }, index) => {return tools.map((item) => ({ ...item, serverName: index })); // serverName是服務器的index所引致});const toolsArr = nestedArr.flat();// console.log("obj", JSON.stringify(toolsArr, null, 2));// 生成工具數組this.tools = toolsArr.map((t) => ({name: t.name, // 工具名稱description: t.description || "", // 工具描述parameters: t.inputSchema, // 工具參數定義Schema的格式execute: async (args: any) => {// 執行工具的邏輯const result = await this.mcpServer[t.serverName].callTool({name: t.name,arguments: args,});return result.content as string;},}));console.log("已連接 MCP Server,工具:",this.tools.map((t) => t.name).join(", "));}

完整代碼

// mcp-client-cli.ts
import axios from "axios"; // 引入axios用于發送HTTP請求
import readline from "readline/promises"; // 獲取用戶的輸入命令
import dotenv from "dotenv"; // 用于加載環境變量配置文件(.env)
import { Client } from "@modelcontextprotocol/sdk/client/index.js"; // MCP Client 的核心類
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; // StdioClientTransport 是 MCP Client 的傳輸層,// ----------------------------------------------------------------
// server.js
import http from "http";
import url from "url";
// ----------------------------------------------------------------dotenv.config();const QIHOOGPT_KEY = process.env.QIHOOGPT_KEY;
if (!QIHOOGPT_KEY) throw new Error("請在 .env 中配置 QIHOOGPT_KEY");interface ToolCall {id: string;function: {name: string;arguments: string;};
}interface ToolDefinition {name: string;description: string;parameters: Record<string, any>;execute: (args: any) => Promise<string>;
}
const paths = [{name: "mcp",args: "/Users/v-yangziqi1/Desktop/work/mcpserver/mcp/build/index.js",},{name: "mcp1",args: "/Users/v-yangziqi1/Desktop/work/mcpserver/mcp1/build/index.js",},
];
class MCPClient {private model = "360gpt-pro";private tools: ToolDefinition[] = [];private mcpServer: Client[] = [];// 初始化mcp server連接async connectToServer(serverScriptPath?: string) {if (!serverScriptPath) return;const res = await Promise.all(paths.map((item, index) => {this.mcpServer.push(new Client({ name: "mcp-client-cli-" + item.name, version: "1.0.0" }));const mcp = this.mcpServer[index]; // 創建 MCP Client 實例// 判斷文件類型const state = item.args.endsWith(".py")? process.platform === "win32"? "python": "python3": process.execPath;//  建立mcp傳輸層const transport = new StdioClientTransport({command: state,args: [item.args],});mcp.connect(transport); // connect 方法用于連接到 MCP Server。// 獲取 MCP Server 上的所有工具名稱return mcp.listTools();}));const nestedArr = res.map(({ tools }, index) => {return tools.map((item) => ({ ...item, serverName: index })); // serverName是服務器的index所引致});const toolsArr = nestedArr.flat();// console.log("obj", JSON.stringify(toolsArr, null, 2));// 生成工具數組this.tools = toolsArr.map((t) => ({name: t.name, // 工具名稱description: t.description || "", // 工具描述parameters: t.inputSchema, // 工具參數定義Schema的格式execute: async (args: any) => {// 執行工具的邏輯const result = await this.mcpServer[t.serverName].callTool({name: t.name,arguments: args,});return result.content as string;},}));console.log("已連接 MCP Server,工具:",this.tools.map((t) => t.name).join(", "));}// 將工具定義轉換為模型可以理解的格式private convertTool(tool: ToolDefinition) {return {type: "function",function: {name: tool.name,description: tool.description,parameters: tool.parameters,},};}// 360模型的方法private async call360GPT(payload: any) {const { data } = await axios.post("https://api.360.cn/v1/chat/completions",{ ...payload, stream: false },{headers: {"Content-Type": "application/json",Authorization: `Bearer ${QIHOOGPT_KEY}`,},});return data;}// 處理用戶查詢// 該方法會將用戶的查詢發送到360GPT模型,并處理返回的async processQuery(query: string) {const messages: any[] = [{ role: "user", content: query }];// 發送用戶消息和mcp server的tools到360GPT模型const res = await this.call360GPT({model: this.model, // 模型名稱messages, // 用戶消息tools: this.tools.map((t) => this.convertTool(t)), // 工具列表tool_choice: "auto", // 自動選擇工具});const choice = res.choices[0]; // 獲取模型返回的第一個選擇const toolCalls = choice.message.tool_calls as ToolCall[] | undefined; // 獲取工具調用列表console.log(choice);console.log(toolCalls);// 如果有工具調用,則執行工具if (toolCalls?.length) {console.log("工具調用列表:", toolCalls);for (const call of toolCalls) {// 在 tools 數組中查找與調用名稱匹配的工具const tool = this.tools.find((t) => t.name === call.function.name);if (!tool) continue; // 如果沒有找到對應的工具,跳過const args = JSON.parse(call.function.arguments || "{}"); // 解析工具調用的參數const result = await tool.execute(args); // 執行工具并獲取結果messages.push({ role: "user", content: result }); // 將工具結果添加到消息中console.log(messages);// 再次調用360GPT模型,傳入更新后的消息進行潤色console.log("🤖 正在思考...");const final = await this.call360GPT({ model: this.model, messages });// 如果有工具調用結果,則返回最終的內容return final.choices[0].message.content;}}return choice.message.content;}// 聊天循環方法async chatLoop() {// 創建 readline 接口,用于讀取用戶輸入const rl = readline.createInterface({input: process.stdin,output: process.stdout,});console.log("輸入內容,輸入 quit 退出:");while (true) {const query = await rl.question("請輸入: "); // 提示用戶輸入查詢內容if (query.toLowerCase() === "quit") break;console.log("🤖 正在思考...");const ans = await this.processQuery(query);console.log("\nAI:", ans, "\n");}rl.close();}
}(async () => {const mcpClient = new MCPClient();const scriptArg = process.argv[2];// 初始化if (scriptArg) await mcpClient.connectToServer(scriptArg);// 服務器監聽// await mcpClient.chatLoop();// process.exit(0);// ----------------------------------------------------------------// server.js// 創建一個簡單的服務器const server = http.createServer(async (req: any, res: any) => {const parsedUrl = url.parse(req.url, true); // 解析 url(含 query 參數)const name = parsedUrl.query.name; // ? 獲取 ?name=xxx 的值res.setHeader("Access-Control-Allow-Origin", "*"); // 允許跨域res.setHeader("Content-Type", "application/json; charset=utf-8");// 簡單路由匹配if (req.method === "GET" && parsedUrl.pathname === "/api/hello") {console.log("收到請求參數 name =", name);const ans = await mcpClient.processQuery(name as string);console.log("AI 回復:", ans);res.end(JSON.stringify({ message: ans }));} else {res.statusCode = 404;res.end(JSON.stringify({ error: "接口未找到" }));}});// ----------------------------------------------------------------// 啟動服務器server.listen(3002, () => {console.log("🚀 本地服務器啟動成功:http://127.0.0.1:3002");});
})();

驗證是否成功

在這里插入圖片描述
已經獲取到兩個倉庫的參數了
trae的倉庫
在這里插入圖片描述

開始調用

這樣就稱得上是成功了
在這里插入圖片描述

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/913672.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/913672.shtml
英文地址,請注明出處:http://en.pswp.cn/news/913672.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

香港站群服務器與普通香港服務器對比

在選擇香港服務器時&#xff0c;用戶常常會遇到"站群服務器"和"普通服務器"兩種選項&#xff0c;雖然它們都基于香港數據中心的基礎設施&#xff0c;但在 IP 地址配置、功能定位和管理復雜度、成本上存在顯著差異&#xff0c;理解這些差異有助于用戶根據實…

4.B樹和B+樹的區別?為什么MySQL選擇B+樹作為索引?

區別&#xff1a;1.數據存儲位置B樹每個節點都存儲了索引和數據B樹只有葉子節點存儲數據&#xff0c;非葉子節點僅存儲索引2.葉子節點的鏈接B樹的所有葉子節點通過指針連接成一個雙向鏈表&#xff0c;可以高效地進行范圍查詢或者順序遍歷B樹則沒有這樣的連接關系&#xff0c;查…

轉換狂魔,Modbus TCP轉Profinet網關打通視覺傳感線連接之路

在汽車零部件沖壓生產線的世界中&#xff0c;液壓機的壓力穩定性是確保產品質量的秘密武器。然而&#xff0c;舊時代的人工巡檢和傳統監測方式卻好似拖累現代化進程的沉重枷鎖&#xff1a;效率低、成本高&#xff0c;還總是趕不上實時反饋的快車。這時&#xff0c;工廠決心大刀…

C++進階—二叉樹進階

第一章&#xff1a;內容安排說明 map和set特性需要先鋪墊二叉搜索樹&#xff0c;而二叉搜索樹也是一種樹形結構二叉搜索樹的特性了解&#xff0c;有助于更好的理解map和set的特性二叉樹中部分面試題稍微有點難度&#xff0c;在前面講解大家不容易接受&#xff0c;且時間長容易…

驅動下一代E/E架構的神經脈絡進化—10BASE-T1S

汽車電子電氣架構的演進正經歷一場深刻的變革&#xff0c;“中央計算單元區域控制器”的架構模式已成為當前主流車型平臺發展的明確方向。這種從傳統的“功能域”&#xff08;Domain&#xff09;架構向“區域”&#xff08;Zonal&#xff09;架構的轉型升級&#xff0c;旨在實現…

某學校系統中挖礦病毒應急排查

本篇文章主要記錄某學校長期未運營維護的程序&#xff0c;被黑客發現了漏洞&#xff0c;但好在學校有全流量設備&#xff0c;抓取到了過程中的流量包 需要你進行上機以及結合流量分析&#xff0c;排查攻擊者利用的漏洞以及上傳利用成功的木馬 文章目錄靶機介紹1.使用工具分析共…

vue 、react前端頁面支持縮放,echarts、地圖點擊左邊不準的原因和解決辦法

原因 由于以上都是通過canvas畫布生成的&#xff0c;一旦初始化&#xff0c;就會按照比例進行縮放&#xff0c;但與此同時&#xff0c;比例尺并沒有變化&#xff0c;導致坐標偏移 解決辦法 設置一個zoomVal產量&#xff0c;在頁面加載時計算縮放比例&#xff0c;然后在canvas容…

(LeetCode 每日一題) 1353. 最多可以參加的會議數目 (優先隊列、小頂堆)

題目&#xff1a;1353. 最多可以參加的會議數目 思路&#xff1a;優先隊列實現小頂堆&#xff0c;0(mx*logn) 在第i天&#xff0c;優先選endDay最小的那一個活動進行。那么遍歷每一天&#xff0c;用小頂堆來維護每個活動的最后一天即可&#xff0c;細節看注釋。 C版本&#xf…

Java結構型模式---代理模式

代理模式基礎概念代理模式是一種結構型設計模式&#xff0c;其核心思想是通過創建一個代理對象來控制對另一個真實對象的訪問。代理對象在客戶端和真實對象之間起到中介作用&#xff0c;允許在不改變真實對象的前提下&#xff0c;對其進行增強或控制。代理模式的核心組件主題接…

MySQL流程控制函數全解析

MySQL 中的流程控制函數&#xff08;也稱為條件函數&#xff09;允許你在 SQL 語句中進行邏輯判斷&#xff0c;根據不同的條件返回不同的值或執行不同的操作。它們極大地增強了 SQL 的靈活性和表達能力&#xff0c;尤其在進行數據轉換、結果格式化、條件聚合和復雜業務邏輯實現…

【7】PostgreSQL 事務

【7】PostgreSQL 事務前言使用事務事務內錯誤處理事務保存點DDL 事務前言 在 PostgreSQL 中&#xff0c;每一個操作都是一個事務。即使一個簡單的查詢(select)&#xff0c;這也是一個事務。 例如&#xff1a; postgres# select now();now --------------------…

Linux:多線程---深入互斥淺談同步

文章目錄1. 互斥1.1 為什么需要互斥1.2 互斥鎖1.3 初談互斥與同步1.4 鎖的原理1.5 可重入VS線程安全1.6 死鎖1.7 避免死鎖的算法&#xff08;擴展&#xff09;序&#xff1a;在上一章中我們知道了線程控制的三個角度&#xff1a;線程創建、線程等待和線程終止&#xff0c;分別從…

適用于 vue2、vue3 的自定義指定:v-int(正整數)

在項目中&#xff0c;我們經常會遇到輸入框只允許輸入數字的情況&#xff0c;下面是一段自定義指定 代碼&#xff0c;復制到項目中&#xff0c;注冊指定即可使用用法如下&#xff1a; 創建一個IntInput.js 文件&#xff0c;將下面代碼復制到文件中保存在項目中的 main.js 文件中…

學習基于springboot秒殺系統-環境配置(接口封裝,mybatis,mysql,redis(Linux))

文章目錄前言創建springboot項目封裝controller層輸入輸出rest api 的json輸出返回頁面集成mybatis集成redis下載虛擬機和centos下載redis.tar.gz上傳redis.tar.gz 到虛擬機前言 今天開始記錄學習秒殺系統-課程是基于慕課上的搜索秒殺系統的課程&#xff0c;老師講解非常好。這…

stm32達到什么程度叫精通?

STM32達到什么程度叫精通&#xff1f;一個十年老兵的深度反思 前言&#xff1a;精通二字&#xff0c;重如泰山 每次有人問我"STM32達到什么程度叫精通"這個問題&#xff0c;我都會沉默很久。 不是因為這個問題難回答&#xff0c;而是因為"精通"這兩個字太重…

微軟上線Deep Research:OpenAI同款智能體,o3+必應雙王炸

今天凌晨&#xff0c;微軟在官網宣布&#xff0c;Azure AI Foundry中上線Deep Research公開預覽版。這是支持API和SDK的OpenAI 高級智能體研究能力產品&#xff0c;并且Azure 的企業級智能體平臺完全集成。Deep Research是OpenAI在今年4月25日發布的最新產品&#xff0c;能夠像…

Spring Batch終極指南:原理、實戰與性能優化

&#x1f31f; Spring Batch終極指南&#xff1a;原理、實戰與性能優化單機日處理10億數據&#xff1f;揭秘企業級批處理架構的核心引擎&#xff01;一、Spring Batch 究竟是什么&#xff1f;Spring batch是用于創建批處理應用程序&#xff08;執行一系列作業&#xff09;的開源…

【Part 3 Unity VR眼鏡端播放器開發與優化】第四節|高分辨率VR全景視頻播放性能優化

文章目錄《VR 360全景視頻開發》專欄Part 3&#xff5c;Unity VR眼鏡端播放器開發與優化第一節&#xff5c;基于Unity的360全景視頻播放實現方案第二節&#xff5c;VR眼鏡端的開發適配與交互設計第三節&#xff5c;Unity?VR手勢交互開發與深度優化第四節&#xff5c;高分辨率V…

TCP/IP協議基礎

TCPIP協議基礎 網絡模型 -OSI參考模型 -OSI參考模型各層功能 -TCP/IP網絡模型 -TCP/IP協議棧OSI參考模型 – 為了解決網絡設備之間的兼容性問題&#xff0c;國際標準化組織ISO于1984年提出了OSI RM&#xff08;開放系統互連參考模型&#xff09;。 OSI參考模型一共有七層&#…

【Nginx】Nginx代理WebSocket

1.websocketWebSocket 是一種網絡通信協議&#xff0c;它提供了在單個 TCP 連接上進行全雙工&#xff08;雙向&#xff09;通信的能力假設需求&#xff1a;把 ws://192.168.0.1:8088/ws-api/websocket/pushData代理到ws://192.168.0.156:8888/websocket/pushData&#xff1b;同…