Files
cocos-mcp/source/test/mcp-tool-tester.ts
2025-07-17 18:12:56 +08:00

235 lines
9.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

declare const Editor: any;
/**
* MCP 工具测试器 - 直接测试通过 WebSocket 的 MCP 工具
*/
export class MCPToolTester {
private ws: WebSocket | null = null;
private messageId = 0;
private responseHandlers = new Map<number, (response: any) => void>();
async connect(port: number): Promise<boolean> {
return new Promise((resolve) => {
try {
this.ws = new WebSocket(`ws://localhost:${port}`);
this.ws.onopen = () => {
console.log('WebSocket 连接成功');
resolve(true);
};
this.ws.onerror = (error) => {
console.error('WebSocket 连接错误:', error);
resolve(false);
};
this.ws.onmessage = (event) => {
try {
const response = JSON.parse(event.data);
if (response.id && this.responseHandlers.has(response.id)) {
const handler = this.responseHandlers.get(response.id);
this.responseHandlers.delete(response.id);
handler?.(response);
}
} catch (error) {
console.error('处理响应时出错:', error);
}
};
} catch (error) {
console.error('创建 WebSocket 时出错:', error);
resolve(false);
}
});
}
async callTool(tool: string, args: any = {}): Promise<any> {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
throw new Error('WebSocket 未连接');
}
return new Promise((resolve, reject) => {
const id = ++this.messageId;
const request = {
jsonrpc: '2.0',
id,
method: 'tools/call',
params: {
name: tool,
arguments: args
}
};
const timeout = setTimeout(() => {
this.responseHandlers.delete(id);
reject(new Error('请求超时'));
}, 10000);
this.responseHandlers.set(id, (response) => {
clearTimeout(timeout);
if (response.error) {
reject(new Error(response.error.message));
} else {
resolve(response.result);
}
});
this.ws!.send(JSON.stringify(request));
});
}
async listTools(): Promise<any> {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
throw new Error('WebSocket 未连接');
}
return new Promise((resolve, reject) => {
const id = ++this.messageId;
const request = {
jsonrpc: '2.0',
id,
method: 'tools/list'
};
const timeout = setTimeout(() => {
this.responseHandlers.delete(id);
reject(new Error('请求超时'));
}, 10000);
this.responseHandlers.set(id, (response) => {
clearTimeout(timeout);
if (response.error) {
reject(new Error(response.error.message));
} else {
resolve(response.result);
}
});
this.ws!.send(JSON.stringify(request));
});
}
async testMCPTools() {
console.log('\n=== 测试 MCP 工具(通过 WebSocket===');
try {
// 0. 获取工具列表
console.log('\n0. 获取工具列表...');
const toolsList = await this.listTools();
console.log(`找到 ${toolsList.tools?.length || 0} 个工具:`);
if (toolsList.tools) {
for (const tool of toolsList.tools.slice(0, 10)) { // 只显示前10个
console.log(` - ${tool.name}: ${tool.description}`);
}
if (toolsList.tools.length > 10) {
console.log(` ... 还有 ${toolsList.tools.length - 10} 个工具`);
}
}
// 1. 测试场景工具
console.log('\n1. 测试当前场景信息...');
const sceneInfo = await this.callTool('scene_get_current_scene');
console.log('场景信息:', JSON.stringify(sceneInfo).substring(0, 100) + '...');
// 2. 测试场景列表
console.log('\n2. 测试场景列表...');
const sceneList = await this.callTool('scene_get_scene_list');
console.log('场景列表:', JSON.stringify(sceneList).substring(0, 100) + '...');
// 3. 测试节点创建
console.log('\n3. 测试创建节点...');
const createResult = await this.callTool('node_create_node', {
name: 'MCPTestNode_' + Date.now(),
nodeType: 'cc.Node',
position: { x: 0, y: 0, z: 0 }
});
console.log('创建节点结果:', createResult);
// 解析创建节点的结果
let nodeUuid: string | null = null;
if (createResult.content && createResult.content[0] && createResult.content[0].text) {
try {
const resultData = JSON.parse(createResult.content[0].text);
if (resultData.success && resultData.data && resultData.data.uuid) {
nodeUuid = resultData.data.uuid;
console.log('成功获取节点UUID:', nodeUuid);
}
} catch (e) {
}
}
if (nodeUuid) {
// 4. 测试查询节点
console.log('\n4. 测试查询节点...');
const queryResult = await this.callTool('node_get_node_info', {
uuid: nodeUuid
});
console.log('节点信息:', JSON.stringify(queryResult).substring(0, 100) + '...');
// 5. 测试删除节点
console.log('\n5. 测试删除节点...');
const removeResult = await this.callTool('node_delete_node', {
uuid: nodeUuid
});
console.log('删除结果:', removeResult);
} else {
console.log('无法从创建结果获取节点UUID尝试通过名称查找...');
// 备用方案:通过名称查找刚创建的节点
const findResult = await this.callTool('node_find_node_by_name', {
name: 'MCPTestNode_' + Date.now()
});
if (findResult.content && findResult.content[0] && findResult.content[0].text) {
try {
const findData = JSON.parse(findResult.content[0].text);
if (findData.success && findData.data && findData.data.uuid) {
nodeUuid = findData.data.uuid;
console.log('通过名称查找成功获取UUID:', nodeUuid);
}
} catch (e) {
}
}
if (!nodeUuid) {
console.log('所有方式都无法获取节点UUID跳过后续节点操作测试');
}
}
// 6. 测试项目工具
console.log('\n6. 测试项目信息...');
const projectInfo = await this.callTool('project_get_project_info');
console.log('项目信息:', JSON.stringify(projectInfo).substring(0, 100) + '...');
// 7. 测试预制体工具
console.log('\n7. 测试预制体列表...');
const prefabResult = await this.callTool('prefab_get_prefab_list', {
folder: 'db://assets'
});
console.log('找到预制体:', prefabResult.data?.length || 0);
// 8. 测试组件工具
console.log('\n8. 测试可用组件...');
const componentsResult = await this.callTool('component_get_available_components');
console.log('可用组件:', JSON.stringify(componentsResult).substring(0, 100) + '...');
// 9. 测试调试工具
console.log('\n9. 测试编辑器信息...');
const editorInfo = await this.callTool('debug_get_editor_info');
console.log('编辑器信息:', JSON.stringify(editorInfo).substring(0, 100) + '...');
} catch (error) {
console.error('MCP 工具测试失败:', error);
}
}
disconnect() {
if (this.ws) {
this.ws.close();
this.ws = null;
}
this.responseHandlers.clear();
}
}
// 导出到全局方便测试
(global as any).MCPToolTester = MCPToolTester;