fix: mcp serve
This commit is contained in:
parent
5389345a00
commit
e5ce8c20b5
|
|
@ -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 模式');
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue