fix: mcp serve

This commit is contained in:
hypercross 2026-03-18 15:00:13 +08:00
parent 5389345a00
commit e5ce8c20b5
2 changed files with 78 additions and 16 deletions

View File

@ -24,7 +24,7 @@ import {
readResource, readResource,
type DocResource type DocResource
} from '../resources/docs.js'; } from '../resources/docs.js';
import { serveCommand } from './serve.js'; import { createContentServer, type ContentServer } from './serve.js';
/** /**
* MCP * MCP
@ -69,12 +69,8 @@ async function mcpServeAction(host: string, options: MCPOptions) {
process.chdir(cwd); process.chdir(cwd);
console.error(`MCP 服务器工作目录:${cwd}`); console.error(`MCP 服务器工作目录:${cwd}`);
// 启动 serve 服务器(在后台运行) // 启动内容服务器(预览服务器)
console.error('启动预览服务器...'); const contentServer: ContentServer = createContentServer(cwd, 3000);
serveCommand(cwd, { port: '3000' });
// 等待服务器启动
await new Promise(resolve => setTimeout(resolve, 1000));
// 动态导入 MCP SDK // 动态导入 MCP SDK
const { Server } = await import('@modelcontextprotocol/sdk/server/index.js'); const { Server } = await import('@modelcontextprotocol/sdk/server/index.js');
@ -328,7 +324,7 @@ async function mcpServeAction(host: string, options: MCPOptions) {
} }
case 'deck_card_crud': { case 'deck_card_crud': {
const params = args as unknown as CardCrudParams; let params = args as unknown as CardCrudParams;
if (!params.csv_file || !params.action) { if (!params.csv_file || !params.action) {
return { return {
@ -337,6 +333,15 @@ async function mcpServeAction(host: string, options: MCPOptions) {
}; };
} }
// 解析 cards 参数(可能是 JSON 字符串)
if (params.cards && typeof params.cards === 'string') {
try {
params.cards = JSON.parse(params.cards);
} catch (e) {
// 如果不是 JSON保持原样
}
}
const result = cardCrud(params); const result = cardCrud(params);
return { return {
@ -465,6 +470,19 @@ async function mcpServeAction(host: string, options: MCPOptions) {
const transport = new StdioServerTransport(); const transport = new StdioServerTransport();
await server.connect(transport); await server.connect(transport);
console.error('TTRPG 卡牌生成 MCP 服务器已启动stdio'); console.error('TTRPG 卡牌生成 MCP 服务器已启动stdio');
// 监听进程退出事件,关闭服务器
process.on('SIGINT', () => {
console.error('\n正在关闭服务器...');
contentServer.close();
process.exit(0);
});
process.on('SIGTERM', () => {
console.error('\n正在关闭服务器...');
contentServer.close();
process.exit(0);
});
} else { } else {
// TODO: 支持 HTTP 传输 // TODO: 支持 HTTP 传输
console.error('HTTP 传输模式尚未实现,请使用 stdio 模式'); console.error('HTTP 传输模式尚未实现,请使用 stdio 模式');

View File

@ -47,7 +47,7 @@ function getMimeType(filePath: string): string {
/** /**
* .md * .md
*/ */
function scanDirectory(dir: string): ContentIndex { export function scanDirectory(dir: string): ContentIndex {
const index: ContentIndex = {}; const index: ContentIndex = {};
function scan(currentPath: string, relativePath: string) { function scan(currentPath: string, relativePath: string) {
@ -176,10 +176,35 @@ function createRequestHandler(
} }
/** /**
* *
*/ */
export const serveCommand: ServeCommandHandler = async (dir, options) => { export interface ContentServer {
const contentDir = resolve(dir); /**
* HTTP
*/
server: Server;
/**
*
*/
watcher: ReturnType<typeof watch>;
/**
*
*/
index: ContentIndex;
/**
*
*/
close(): void;
}
/**
*
*/
export function createContentServer(
contentDir: string,
port: number,
distPath: string = distDir,
): ContentServer {
let contentIndex: ContentIndex = {}; let contentIndex: ContentIndex = {};
// 扫描内容目录生成索引 // 扫描内容目录生成索引
@ -231,19 +256,38 @@ export const serveCommand: ServeCommandHandler = async (dir, options) => {
// 创建请求处理器 // 创建请求处理器
const handleRequest = createRequestHandler( const handleRequest = createRequestHandler(
contentDir, contentDir,
distDir, distPath,
() => contentIndex, () => contentIndex,
); );
// 创建 HTTP 服务器 // 创建 HTTP 服务器
const server = createServer(handleRequest); const server = createServer(handleRequest);
const port = parseInt(options.port, 10);
server.listen(port, () => { server.listen(port, () => {
console.log(`\n开发服务器已启动http://localhost:${port}`); console.log(`\n开发服务器已启动http://localhost:${port}`);
console.log(`内容目录:${contentDir}`); console.log(`内容目录:${contentDir}`);
console.log(`静态资源目录:${distDir}`); console.log(`静态资源目录:${distPath}`);
console.log(`索引文件http://localhost:${port}/__CONTENT_INDEX.json\n`); console.log(`索引文件http://localhost:${port}/__CONTENT_INDEX.json\n`);
}); });
return {
server,
watcher,
index: contentIndex,
close() {
console.log("关闭内容服务器...");
server.close();
watcher.close();
},
};
}
/**
*
*/
export const serveCommand: ServeCommandHandler = async (dir, options) => {
const contentDir = resolve(dir);
const port = parseInt(options.port, 10);
createContentServer(contentDir, port);
}; };