MCP服務支持兩種協議,Studio和SSE/HTTP,目前官方提供的SDK有各種語言。
開發方式有以下幾種:
編程語言 | MCP命令 | 協議 | 發布方式 |
---|---|---|---|
Python | uvx | STUDIO | pypi |
Python | 遠程調用 | SSE | 服務器部署 |
Nodejs | pnpm | STUDIO | pnpm |
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