#!/usr/bin/env node
/**
* MCP 抽签工具服务器
*
* 这是一个基于 Model Context Protocol (MCP) 的抽签工具演示
* 提供抽签、投骰子、抛硬币等随机功能
*
* 使用方法:
* 1. 安装依赖:npm install
* 2. 启动服务器:npm start
* 3. 在 MCP Inspector 中连接测试
*/
// 导入 MCP SDK 相关模块
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
/**
* 创建 MCP 服务器实例
*
* @param {Object} serverInfo - 服务器信息
* @param {string} serverInfo.name - 服务器名称
* @param {string} serverInfo.version - 服务器版本
* @param {Object} capabilities - 服务器能力配置
* @param {Object} capabilities.tools - 工具能力配置
*/
const server = new Server(
{
name: 'lottery-tool', // 服务器名称,用于标识
version: '1.0.0', // 服务器版本号
},
{
capabilities: {
tools: {}, // 声明支持工具功能
},
}
);
/**
* 定义工具列表
*
* 每个工具包含:
* - name: 工具名称
* - description: 工具描述
* - inputSchema: 输入参数模式(JSON Schema)
*/
const tools = [
{
name: 'draw_lottery',
description: '从给定的选项列表中随机抽取一个或多个结果',
inputSchema: {
type: 'object',
properties: {
options: {
type: 'array',
items: { type: 'string' },
description: '抽签选项列表',
},
count: {
type: 'number',
description: '抽取数量,默认为1',
default: 1,
},
allow_duplicate: {
type: 'boolean',
description: '是否允许重复抽取,默认为false',
default: false,
},
},
required: ['options'], // 必需参数
},
},
{
name: 'roll_dice',
description: '投掷骰子,支持自定义面数和数量',
inputSchema: {
type: 'object',
properties: {
sides: {
type: 'number',
description: '骰子面数,默认为6',
default: 6,
},
count: {
type: 'number',
description: '骰子数量,默认为1',
default: 1,
},
},
},
},
{
name: 'flip_coin',
description: '抛硬币,返回正面或反面',
inputSchema: {
type: 'object',
properties: {
count: {
type: 'number',
description: '抛硬币次数,默认为1',
default: 1,
},
},
},
},
];
/**
* 处理工具列表请求
*
* 当客户端请求可用工具列表时,返回所有已注册的工具
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools, // 返回工具列表
};
});
/**
* 处理工具调用请求
*
* 根据工具名称和参数执行相应的功能
*
* @param {Object} request - 工具调用请求
* @param {string} request.params.name - 工具名称
* @param {Object} request.params.arguments - 工具参数
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
// 根据工具名称执行相应的功能
switch (name) {
case 'draw_lottery': {
// 抽签工具:从选项列表中随机抽取结果
const { options, count = 1, allow_duplicate = false } = args;
// 参数验证
if (!Array.isArray(options) || options.length === 0) {
throw new Error('选项列表不能为空');
}
if (count > options.length && !allow_duplicate) {
throw new Error('抽取数量不能超过选项数量(不允许重复时)');
}
// 执行抽签逻辑
const results = [];
const availableOptions = [...options]; // 创建选项副本
for (let i = 0; i < count; i++) {
// 随机选择索引
const randomIndex = Math.floor(Math.random() * availableOptions.length);
const selected = availableOptions[randomIndex];
results.push(selected);
// 如果不允许重复,从可用选项中移除已选择的
if (!allow_duplicate) {
availableOptions.splice(randomIndex, 1);
}
}
// 返回格式化的结果
return {
content: [
{
type: 'text',
text: `🎲 抽签结果:\n${results.map((result, index) => `${index + 1}. ${result}`).join('\n')}`,
},
],
};
}
case 'roll_dice': {
// 投骰子工具:模拟投掷骰子
const { sides = 6, count = 1 } = args;
// 参数验证
if (sides < 2) {
throw new Error('骰子面数至少为2');
}
if (count < 1) {
throw new Error('骰子数量至少为1');
}
// 执行投骰子逻辑
const results = [];
let total = 0;
for (let i = 0; i < count; i++) {
// 生成 1 到 sides 之间的随机数
const roll = Math.floor(Math.random() * sides) + 1;
results.push(roll);
total += roll;
}
// 根据骰子数量格式化输出
const resultText = count === 1
? `🎲 投掷结果:${results[0]}`
: `🎲 投掷结果:${results.join(', ')}\n📊 总计:${total}`;
return {
content: [
{
type: 'text',
text: resultText,
},
],
};
}
case 'flip_coin': {
// 抛硬币工具:模拟抛硬币
const { count = 1 } = args;
// 参数验证
if (count < 1) {
throw new Error('抛硬币次数至少为1');
}
// 执行抛硬币逻辑
const results = [];
let heads = 0; // 正面次数
let tails = 0; // 反面次数
for (let i = 0; i < count; i++) {
// 50% 概率生成正面或反面
const result = Math.random() < 0.5 ? '正面' : '反面';
results.push(result);
if (result === '正面') heads++;
else tails++;
}
// 根据抛硬币次数格式化输出
const resultText = count === 1
? `🪙 抛硬币结果:${results[0]}`
: `🪙 抛硬币结果:${results.join(', ')}\n📊 统计:正面 ${heads} 次,反面 ${tails} 次`;
return {
content: [
{
type: 'text',
text: resultText,
},
],
};
}
default:
// 未知工具名称
throw new Error(`未知工具:${name}`);
}
} catch (error) {
// 错误处理:返回错误信息
return {
content: [
{
type: 'text',
text: `❌ 错误:${error.message}`,
},
],
isError: true,
};
}
});
/**
* 启动服务器
*
* 使用标准输入输出传输协议启动 MCP 服务器
* 这种方式适合与 AI 客户端(如 Claude Desktop)集成
*/
async function main() {
try {
// 创建标准输入输出传输实例
const transport = new StdioServerTransport();
// 连接服务器到传输层
await server.connect(transport);
// 输出启动信息(使用 stderr 避免干扰 MCP 协议)
console.error('🎲 抽签工具 MCP 服务器已启动');
console.error('📡 传输方式:标准输入输出 (stdio)');
console.error('🔧 可用工具:draw_lottery, roll_dice, flip_coin');
} catch (error) {
console.error('❌ 服务器启动失败:', error);
process.exit(1);
}
}
// 启动服务器并处理错误
main().catch((error) => {
console.error('❌ 服务器运行错误:', error);
process.exit(1);
});