【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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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的三大范式:

目录 键和相关属性的概念: 第一范式: 第二范式: 第三范式: 总结: 反范式化: 在关系型数据库中,关于数据表设计的基本原则,规则就称为范式。 范式是关系数据库理论的基础&…