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 node
import { 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技术栈。
总结
工具 (Tools):
echo
- 回显文本get_system_info
- 获取系统信息calculate
- 数学计算
资源 (Resources):
studio://server-info
- 服务器信息studio://capabilities
- 服务器能力列表
使用方法
# 开发模式
pnpm dev
# 构建项目
pnpm build
# 运行服务器
pnpm start
# 测试验证服务调用
node test-client.cjs