【MCP開發】Nodejs+Typescript+pnpm+Studio搭建Mcp服務

MCP服務支持兩種協議,Studio和SSE/HTTP,目前官方提供的SDK有各種語言。
在這里插入圖片描述
開發方式有以下幾種:

編程語言MCP命令協議發布方式
PythonuvxSTUDIOpypi
Python遠程調用SSE服務器部署
NodejspnpmSTUDIOpnpm
Nodejs遠程調用SSE服務器部署

一、初始化項目結構和配置文件

1、創建package.json文件來初始化項目配置

{"name": "wjb-mcp-server-studio","version": "1.0.0","description": "A local MCP server based on Studio protocol using Node.js and TypeScript","main": "dist/index.js","type": "module","scripts": {"build": "tsc","dev": "tsx src/index.ts","start": "node dist/index.js","clean": "rimraf dist","type-check": "tsc --noEmit"},"keywords": ["mcp","studio","server","typescript","nodejs"],"author": "Your Name","license": "MIT","devDependencies": {"@types/node": "^20.0.0","rimraf": "^5.0.0","tsx": "^4.0.0","typescript": "^5.0.0"},"dependencies": {"@modelcontextprotocol/sdk": "^0.5.0"},"engines": {"node": ">=18.0.0"}
}

2、創建TypeScript配置文件

{"compilerOptions": {"target": "ES2022","module": "ESNext","moduleResolution": "node","lib": ["ES2022"],"outDir": "./dist","rootDir": "./src","strict": true,"esModuleInterop": true,"skipLibCheck": true,"forceConsistentCasingInFileNames": true,"declaration": true,"declarationMap": true,"sourceMap": true,"removeComments": false,"noImplicitAny": true,"noImplicitReturns": true,"noImplicitThis": true,"noUnusedLocals": true,"noUnusedParameters": true,"exactOptionalPropertyTypes": true,"noImplicitOverride": true,"noPropertyAccessFromIndexSignature": true,"noUncheckedIndexedAccess": true,"allowUnusedLabels": false,"allowUnreachableCode": false},"include": ["src/**/*"],"exclude": ["node_modules","dist"]
}

3、創建src目錄結構并實現MCP服務器的主入口文件

#!/usr/bin/env nodeimport { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {CallToolRequestSchema,ErrorCode,ListResourcesRequestSchema,ListToolsRequestSchema,McpError,ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';// 服務器信息
const SERVER_NAME = 'wjb-mcp-server-studio';
const SERVER_VERSION = '1.0.0';// 創建服務器實例
const server = new Server({name: SERVER_NAME,version: SERVER_VERSION,},{capabilities: {resources: {},tools: {},},}
);// 工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => {return {tools: [{name: 'echo',description: 'Echo back the input text',inputSchema: {type: 'object',properties: {text: {type: 'string',description: 'Text to echo back',},},required: ['text'],},},{name: 'get_system_info',description: 'Get basic system information',inputSchema: {type: 'object',properties: {},},},{name: 'calculate',description: 'Perform basic mathematical calculations',inputSchema: {type: 'object',properties: {expression: {type: 'string',description: 'Mathematical expression to evaluate (e.g., "2 + 3 * 4")',},},required: ['expression'],},},],};
});// 工具調用處理
server.setRequestHandler(CallToolRequestSchema, async (request) => {const { name, arguments: args } = request.params;switch (name) {case 'echo': {const text = args?.['text'] as string;if (!text) {throw new McpError(ErrorCode.InvalidParams, 'Missing required parameter: text');}return {content: [{type: 'text',text: `Echo: ${text}`,},],};}case 'get_system_info': {const systemInfo = {platform: process.platform,arch: process.arch,nodeVersion: process.version,uptime: process.uptime(),memoryUsage: process.memoryUsage(),timestamp: new Date().toISOString(),};return {content: [{type: 'text',text: JSON.stringify(systemInfo, null, 2),},],};}case 'calculate': {const expression = args?.['expression'] as string;if (!expression) {throw new McpError(ErrorCode.InvalidParams, 'Missing required parameter: expression');}try {// 簡單的數學表達式計算(僅支持基本運算符)const sanitizedExpression = expression.replace(/[^0-9+\-*/().\s]/g, '');if (sanitizedExpression !== expression) {throw new Error('Invalid characters in expression');}// 使用 Function 構造器安全地計算表達式const result = Function(`"use strict"; return (${sanitizedExpression})`)();return {content: [{type: 'text',text: `${expression} = ${result}`,},],};} catch (error) {throw new McpError(ErrorCode.InternalError,`Failed to calculate expression: ${error instanceof Error ? error.message : 'Unknown error'}`);}}default:throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);}
});// 資源列表
server.setRequestHandler(ListResourcesRequestSchema, async () => {return {resources: [{uri: 'studio://server-info',mimeType: 'application/json',name: 'Server Information',description: 'Information about this MCP server',},{uri: 'studio://capabilities',mimeType: 'application/json',name: 'Server Capabilities',description: 'List of server capabilities and features',},],};
});// 資源讀取處理
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {const { uri } = request.params;switch (uri) {case 'studio://server-info': {const serverInfo = {name: SERVER_NAME,version: SERVER_VERSION,description: 'A local MCP server based on Studio protocol',author: 'Your Name',capabilities: ['tools', 'resources'],uptime: process.uptime(),timestamp: new Date().toISOString(),};return {contents: [{uri,mimeType: 'application/json',text: JSON.stringify(serverInfo, null, 2),},],};}case 'studio://capabilities': {const capabilities = {tools: {count: 3,available: ['echo', 'get_system_info', 'calculate'],},resources: {count: 2,available: ['studio://server-info', 'studio://capabilities'],},features: {stdio_transport: true,json_rpc: true,error_handling: true,},};return {contents: [{uri,mimeType: 'application/json',text: JSON.stringify(capabilities, null, 2),},],};}default:throw new McpError(ErrorCode.InvalidParams, `Unknown resource: ${uri}`);}
});// 啟動服務器
async function main() {const transport = new StdioServerTransport();await server.connect(transport);// 優雅關閉處理process.on('SIGINT', async () => {await server.close();process.exit(0);});process.on('SIGTERM', async () => {await server.close();process.exit(0);});
}// 錯誤處理
process.on('uncaughtException', (error) => {console.error('Uncaught Exception:', error);process.exit(1);
});process.on('unhandledRejection', (reason, promise) => {console.error('Unhandled Rejection at:', promise, 'reason:', reason);process.exit(1);
});// 啟動服務器
main().catch((error) => {console.error('Failed to start server:', error);process.exit(1);
});

二、安裝必要的依賴包

執行安裝命令:

# 切換到淘寶鏡像
# pnpm config set registry https://registry.npmjs.org/ # 安裝
pnpm i

三、構建并啟動服務

1、構建

pnpm build

構建完成后,在根目錄生成dist目錄
在這里插入圖片描述

2、啟動

# echo '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {"tools": {}}}}' | pnpm dev # 或直接運行 
pnpm dev 

四、測試MCP服務器功能

1、測試代碼

根目錄:/test-client.cjs

#!/usr/bin/env node// 簡單的MCP客戶端測試腳本
const { spawn } = require('child_process');
const readline = require('readline');// 啟動MCP服務器
const server = spawn('node', ['dist/index.js'], {stdio: ['pipe', 'pipe', 'inherit']
});// 創建readline接口來處理服務器響應
const rl = readline.createInterface({input: server.stdout,crlfDelay: Infinity
});// 監聽服務器響應
rl.on('line', (line) => {console.log('服務器響應:', line);
});// 發送測試請求的函數
function sendRequest(request) {console.log('發送請求:', JSON.stringify(request));server.stdin.write(JSON.stringify(request) + '\n');
}// 等待一下然后發送測試請求
setTimeout(() => {// 1. 初始化請求sendRequest({jsonrpc: '2.0',id: 1,method: 'initialize',params: {protocolVersion: '2024-11-05',capabilities: { tools: {} },clientInfo: {name: 'test-client',version: '1.0.0'}}});// 2. 列出工具setTimeout(() => {sendRequest({jsonrpc: '2.0',id: 2,method: 'tools/list'});}, 1000);// 3. 調用echo工具setTimeout(() => {sendRequest({jsonrpc: '2.0',id: 3,method: 'tools/call',params: {name: 'echo',arguments: {text: 'Hello, MCP Server!'}}});}, 2000);// 4. 調用計算工具setTimeout(() => {sendRequest({jsonrpc: '2.0',id: 4,method: 'tools/call',params: {name: 'calculate',arguments: {expression: '2 + 3 * 4'}}});}, 3000);// 5. 列出資源setTimeout(() => {sendRequest({jsonrpc: '2.0',id: 5,method: 'resources/list'});}, 4000);// 6. 讀取資源setTimeout(() => {sendRequest({jsonrpc: '2.0',id: 6,method: 'resources/read',params: {uri: 'studio://server-info'}});}, 5000);// 7. 關閉服務器setTimeout(() => {console.log('\n測試完成,關閉服務器...');server.kill();process.exit(0);}, 6000);}, 500);// 錯誤處理
server.on('error', (error) => {console.error('服務器錯誤:', error);
});server.on('close', (code) => {console.log(`服務器進程退出,退出碼: ${code}`);
});

2、運行測試文件

node test-client.cjs 

運行成功,打印如下:

發送請求: {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"clientInfo":{"name":"test-client","version":"1.0.0"}}}
服務器響應: {"result":{"protocolVersion":"2024-11-05","capabilities":{"resources":{},"tools":{}},"serverInfo":{"name":"wjb-mcp-server-studio","version":"1.0.0"}},"jsonrpc":"2.0","id":1}
發送請求: {"jsonrpc":"2.0","id":2,"method":"tools/list"}
服務器響應: {"result":{"tools":[{"name":"echo","description":"Echo back the input text","inputSchema":{"type":"object","properties":{"text":{"type":"string","description":"Text to echo back"}},"required":["text"]}},{"name":"get_system_info","description":"Get basic system information","inputSchema":{"type":"object","properties":{}}},{"name":"calculate","description":"Perform basic mathematical calculations","inputSchema":{"type":"object","properties":{"expression":{"type":"string","description":"Mathematical expression to evaluate (e.g., \"2 + 3 * 4\")"}},"required":["expression"]}}]},"jsonrpc":"2.0","id":2}
發送請求: {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"echo","arguments":{"text":"Hello, MCP Server!"}}}
服務器響應: {"result":{"content":[{"type":"text","text":"Echo: Hello, MCP Server!"}]},"jsonrpc":"2.0","id":3}
發送請求: {"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"calculate","arguments":{"expression":"2 + 3 * 4"}}}
服務器響應: {"result":{"content":[{"type":"text","text":"2 + 3 * 4 = 14"}]},"jsonrpc":"2.0","id":4}
發送請求: {"jsonrpc":"2.0","id":5,"method":"resources/list"}
服務器響應: {"result":{"resources":[{"uri":"studio://server-info","mimeType":"application/json","name":"Server Information","description":"Information about this MCP server"},{"uri":"studio://capabilities","mimeType":"application/json","name":"Server Capabilities","description":"List of server capabilities and features"}]},"jsonrpc":"2.0","id":5}
發送請求: {"jsonrpc":"2.0","id":6,"method":"resources/read","params":{"uri":"studio://server-info"}}
服務器響應: {"result":{"contents":[{"uri":"studio://server-info","mimeType":"application/json","text":"{\n  \"name\": \"wjb-mcp-server-studio\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A local MCP server based on Studio protocol\",\n  \"author\": \"Your Name\",\n  \"capabilities\": [\n    \"tools\",\n    \"resources\"\n  ],\n  \"uptime\": 5.5274685,\n  \"timestamp\": \"2025-08-13T14:21:45.028Z\"\n}"}]},"jsonrpc":"2.0","id":6}測試完成,關閉服務器...

已經成功基于Studio協議搭建了一個本地MCP服務,使用Node.js + TypeScript + pnpm技術棧。

五、Trae/Cursor中如何調用

通過配置文件 如果 Trae 支持配置文件,添加類似配置:

{"mcpServers": {"wjb-mcp-server-studio": {"command": "node","args": ["d:\\Code\\MCP\\wjb-mcp-server-studio\\dist\\index.js"],"cwd": "d:\\Code\\MCP\\wjb-mcp-server-studio"}}
}

步驟如下:
在這里插入圖片描述
在這里插入圖片描述
如何連接成功,會看到如上圖所示的tool方法。
然后打開智能體對話框,輸入:“查看服務器信息”

在這里插入圖片描述
在這里插入圖片描述

總結

工具 (Tools):

  • echo - 回顯文本
  • get_system_info - 獲取系統信息
  • calculate - 數學計算

資源 (Resources):

  • studio://server-info - 服務器信息
  • studio://capabilities - 服務器能力列表

使用方法

# 開發模式
pnpm dev# 構建項目
pnpm build# 運行服務器
pnpm start# 測試驗證服務調用
node test-client.cjs 

源碼:
https://gitee.com/6feel/mcp_nodejs_studio

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

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

相關文章

vscode使用keil5出現變量跳轉不了和搜索全局不了

vscode使用keil5出現變量跳轉不了,或者未包含文件,或者未全局檢索; 參考如下文章后還會出現; 為什么vscode搜索欄只搜索已經打開的文件_vscode全局搜索只能搜當前文件-CSDN博客 在機緣巧合之下發現如下解決方式: 下載…

命名空間——網絡(net)

命名空間——網絡(net) 一、網絡命名空間:每個都是獨立的“網絡房間” 想象你的電腦是一棟大樓,每個網絡命名空間就是大樓里的一個“獨立房間”: 每個房間里有自己的“網線接口”(網卡)、“門牌…

一文讀懂16英寸筆記本的實際尺寸與最佳應用場景

當您搜索"16寸筆記本電腦長寬"時,內心真正在問的是什么?是背包能否容納?桌面空間是否足夠?還是期待屏幕尺寸與便攜性的完美平衡?這個看似簡單的尺寸數字背后,凝結著計算機制造商對用戶體驗的深刻…

Android Studio中創建Git分支

做一些Android項目時,有時候想要做一些實驗性的修改,這個實驗可能需要很多步驟,所以不是一時半會能完成的,這就需要在實驗的過程中不斷修改代碼,且要提交代碼,方便回滾或比較差異,但是既然是實驗…

內存可見性和偽共享問題

文章目錄什么是內存可見性問題為什么會出現可見性問題解決可見性問題的方法1. 使用volatile關鍵字2. 使用synchronized3. 使用java.util.concurrent包下的原子類什么是偽共享問題CPU緩存行偽共享的危害解決偽共享的方法1. 緩存行填充2. 使用Contended注解(JDK 8&…

Spring MVC 九大組件源碼深度剖析(三):ThemeResolver - 動態換膚的奧秘

文章目錄一、主題機制的核心價值二、核心接口設計三、四大實現類源碼解析1. FixedThemeResolver(固定主題策略)2. CookieThemeResolver(Cookie存儲策略)3. SessionThemeResolver(Session存儲策略)4. Abstra…

一、Docker本地安裝

((這里引用知乎上大佬的說法:https://www.zhihu.com/question/48174633 服務器虛擬化解決的核心問題是資源調配,而容器解決的核心問題是應用開發、測試和部署。 一、參考帖子 Ubuntu 的 |Docker 文檔 【docker】ubuntu完全卸載docker及再次安裝_ubuntu…

LeetCode 分類刷題:2962. 統計最大元素出現至少 K 次的子數組

題目給你一個整數數組 nums 和一個 正整數 k 。請你統計有多少滿足 「 nums 中的 最大 元素」至少出現 k 次的子數組,并返回滿足這一條件的子數組的數目。子數組是數組中的一個連續元素序列。示例 1:輸入:nums [1,3,2,3,3], k 2 輸出&#…

10分鐘掌握swift

整理一個 10分鐘掌握 Swift 的精華指南,用一個 Demo 串聯 Swift 的核心語法、數據結構、函數、類/結構體和閉包,讓你快速入門。1?? 基礎語法與變量import Foundation // 引入基礎庫// 變量和常量 var name: String "Alice" // 可變 let…

【完整源碼+數據集+部署教程】食品分類與實例分割系統源碼和數據集:改進yolo11-AggregatedAttention

背景意義 研究背景與意義 隨著全球食品產業的快速發展,食品安全和質量控制日益成為社會關注的焦點。食品分類與實例分割技術的應用,能夠有效提升食品識別的準確性和效率,為食品監管、營養分析以及智能餐飲等領域提供重要支持。傳統的食品識別…

C# 中的N+1問題

目錄 含義 影響 避免方法 1. 立即加載(Eager Loading) 2. 顯式加載(Explicit Loading) 3. 投影(Projection) 4. 批處理查詢 5. 禁用延遲加載 含義 N1 問題 是 ORM(對象關系映射&#x…

國內多光譜相機做得好的廠家有哪些?-多光譜相機品牌廠家

多光譜相機是一種能夠同時捕捉多個特定波段的光譜信息,這些波段覆蓋可見光、近紅外以及短波紅外等區域。廣泛應用于遙感、農業、環境監測、工業檢測、安防等領域。近年來,我國在多光譜技術領域取得了顯著進步,涌現出一批技術實力強、產品性能…

如何用外部電腦訪問本地網頁?

之前本來說用內網穿透工具來查看完成這個工具,結果感覺各種不符合心意,突然發現有更簡單的方法。如果想讓兩臺電腦在 同一局域網 內都能訪問運行在 http://localhost:5174/ 上的項目,而不需要使用內網穿透工具,可以通過以下方法實…

PromptPilot — AI 自動化任務的下一個環節

作者:陳大魚頭 github:https://github.com/KRISACHAN 郵箱:chenjinwen77@gmail.com PromptPilot 體驗地址:https://promptpilot.volcengine.com/ 前言 如果大家有關注 AI 相關新聞的話,一定會知道在 2025 年 6 月 11 日火山引擎 FORCE 原動力大會上,豆包大模型 1.6 系列…

[Responsive theme color] 動態更新 | CSS變量+JS操控 | 移動端-漢堡菜單 | 實現平滑滾動

第3章:CSS變量操控 歡迎回來🐻??? 通過前兩章,我們掌握了 動態主題定制 的交互邏輯,以及 色彩工具函數 如何實現色值格式轉換。 本章將揭示技術拼圖的最后一塊:CSS變量動態操控,解析JavaScript如何實…

數學建模 15 邏輯回歸與隨機森林

邏輯回歸(用于分類)用途:通過已有數據,計算出線性方程的參數w后,可以用于預測某一個物品屬于某一類的概率,[0,1];求解思想:邏輯回歸通過最大似然估計(Maximum Likelihood Estimation…

衡石使用指南嵌入式場景實踐之儀表盤嵌入

應用展示交互 應用集市展示應用時會與儀表盤、圖表進行交互操作,主要包括去分析、保存當前過濾快照、字段設置、刷新、全屏、嵌入、導出等功能。 保存當前過濾快照 儀表盤展示數據時往往使用過濾器來查看不同場景下的分析數據。用戶從一種場景切換到另一種場景&a…

Qt | 四種方式實現多線程導出數據功能

前言 在以往的項目開發中,在很多地方用到了多線程。針對不同的業務邏輯,需要使用不同的多線程實現方法,來達到優化項目的目的。本文記錄下在Qt開發中用到的多線程技術實現方法,以導出指定范圍的數字到txt文件為例,展示…

運放的學習筆記以及一些用法的個人看法

負反饋形成了虛短。 你的輸出會對-極產生一個向上的電壓,當你的-的時候就兩邊相等了,這個時候就輸出就不變了,也就是負反饋調節,調節了左邊的電壓差 如果你的右邊輸出已經達到了12v或者0v這個時候你就飽和了,這個時候…

MySQL的三大范式:

目錄 鍵和相關屬性的概念: 第一范式: 第二范式: 第三范式: 總結: 反范式化: 在關系型數據庫中,關于數據表設計的基本原則,規則就稱為范式。 范式是關系數據庫理論的基礎&…