初次提交
This commit is contained in:
132
source/test/manual-test.ts
Normal file
132
source/test/manual-test.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
declare const Editor: any;
|
||||
|
||||
/**
|
||||
* 手动测试脚本
|
||||
* 可以在 Cocos Creator 控制台中执行测试
|
||||
*/
|
||||
|
||||
export async function testSceneTools() {
|
||||
console.log('=== Testing Scene Tools ===');
|
||||
|
||||
try {
|
||||
// 1. 获取场景信息
|
||||
console.log('1. Getting scene info...');
|
||||
const sceneInfo = await Editor.Message.request('scene', 'get-scene-info');
|
||||
console.log('Scene info:', sceneInfo);
|
||||
|
||||
// 2. 创建节点
|
||||
console.log('\n2. Creating test node...');
|
||||
const createResult = await Editor.Message.request('scene', 'create-node', {
|
||||
name: 'TestNode_' + Date.now(),
|
||||
type: 'cc.Node'
|
||||
});
|
||||
console.log('Create result:', createResult);
|
||||
|
||||
if (createResult && createResult.uuid) {
|
||||
const nodeUuid = createResult.uuid;
|
||||
|
||||
// 3. 查询节点
|
||||
console.log('\n3. Querying node...');
|
||||
const nodeInfo = await Editor.Message.request('scene', 'query-node', {
|
||||
uuid: nodeUuid
|
||||
});
|
||||
console.log('Node info:', nodeInfo);
|
||||
|
||||
// 4. 设置节点属性
|
||||
console.log('\n4. Setting node position...');
|
||||
await Editor.Message.request('scene', 'set-node-property', {
|
||||
uuid: nodeUuid,
|
||||
path: 'position',
|
||||
value: { x: 100, y: 200, z: 0 }
|
||||
});
|
||||
console.log('Position set successfully');
|
||||
|
||||
// 5. 添加组件
|
||||
console.log('\n5. Adding Sprite component...');
|
||||
const addCompResult = await Editor.Message.request('scene', 'add-component', {
|
||||
uuid: nodeUuid,
|
||||
component: 'cc.Sprite'
|
||||
});
|
||||
console.log('Component added:', addCompResult);
|
||||
|
||||
// 6. 查询组件
|
||||
console.log('\n6. Querying component...');
|
||||
const compInfo = await Editor.Message.request('scene', 'query-node-component', {
|
||||
uuid: nodeUuid,
|
||||
component: 'cc.Sprite'
|
||||
});
|
||||
console.log('Component info:', compInfo);
|
||||
|
||||
// 7. 删除节点
|
||||
console.log('\n7. Removing test node...');
|
||||
await Editor.Message.request('scene', 'remove-node', {
|
||||
uuid: nodeUuid
|
||||
});
|
||||
console.log('Node removed successfully');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Test failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function testAssetTools() {
|
||||
console.log('\n=== Testing Asset Tools ===');
|
||||
|
||||
try {
|
||||
// 1. 查询资源
|
||||
console.log('1. Querying image assets...');
|
||||
const assets = await Editor.Message.request('asset-db', 'query-assets', {
|
||||
pattern: '**/*.png',
|
||||
ccType: 'cc.ImageAsset'
|
||||
});
|
||||
console.log('Found assets:', assets?.length || 0);
|
||||
|
||||
// 2. 获取资源信息
|
||||
console.log('\n2. Getting asset database info...');
|
||||
const assetInfo = await Editor.Message.request('asset-db', 'query-asset-info', {
|
||||
uuid: 'db://assets'
|
||||
});
|
||||
console.log('Asset info:', assetInfo);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Test failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function testProjectTools() {
|
||||
console.log('\n=== Testing Project Tools ===');
|
||||
|
||||
try {
|
||||
// 1. 获取项目信息
|
||||
console.log('1. Getting project info...');
|
||||
const projectInfo = await Editor.Message.request('project', 'query-info');
|
||||
console.log('Project info:', projectInfo);
|
||||
|
||||
// 2. 检查构建能力
|
||||
console.log('\n2. Checking build capability...');
|
||||
const canBuild = await Editor.Message.request('project', 'can-build');
|
||||
console.log('Can build:', canBuild);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Test failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function runAllTests() {
|
||||
console.log('Starting MCP Server Tools Test...\n');
|
||||
|
||||
await testSceneTools();
|
||||
await testAssetTools();
|
||||
await testProjectTools();
|
||||
|
||||
console.log('\n=== All tests completed ===');
|
||||
}
|
||||
|
||||
// 导出到全局,方便在控制台调用
|
||||
(global as any).MCPTest = {
|
||||
testSceneTools,
|
||||
testAssetTools,
|
||||
testProjectTools,
|
||||
runAllTests
|
||||
};
|
||||
235
source/test/mcp-tool-tester.ts
Normal file
235
source/test/mcp-tool-tester.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
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;
|
||||
168
source/test/tool-tester.ts
Normal file
168
source/test/tool-tester.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
declare const Editor: any;
|
||||
|
||||
interface TestResult {
|
||||
tool: string;
|
||||
method: string;
|
||||
success: boolean;
|
||||
result?: any;
|
||||
error?: string;
|
||||
time: number;
|
||||
}
|
||||
|
||||
export class ToolTester {
|
||||
private results: TestResult[] = [];
|
||||
|
||||
async runTest(tool: string, method: string, params: any): Promise<TestResult> {
|
||||
const startTime = Date.now();
|
||||
const result: TestResult = {
|
||||
tool,
|
||||
method,
|
||||
success: false,
|
||||
time: 0
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await Editor.Message.request(tool, method, params);
|
||||
result.success = true;
|
||||
result.result = response;
|
||||
} catch (error) {
|
||||
result.success = false;
|
||||
result.error = error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
result.time = Date.now() - startTime;
|
||||
this.results.push(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
async testSceneOperations() {
|
||||
console.log('Testing Scene Operations...');
|
||||
|
||||
// Test node creation (this is the main scene operation that works)
|
||||
const createResult = await this.runTest('scene', 'create-node', {
|
||||
name: 'TestNode',
|
||||
type: 'cc.Node'
|
||||
});
|
||||
|
||||
if (createResult.success && createResult.result) {
|
||||
const nodeUuid = createResult.result;
|
||||
|
||||
// Test query node info
|
||||
await this.runTest('scene', 'query-node-info', nodeUuid);
|
||||
|
||||
// Test remove node
|
||||
await this.runTest('scene', 'remove-node', nodeUuid);
|
||||
}
|
||||
|
||||
// Test execute scene script
|
||||
await this.runTest('scene', 'execute-scene-script', {
|
||||
name: 'cocos-mcp-server',
|
||||
method: 'test-method',
|
||||
args: []
|
||||
});
|
||||
}
|
||||
|
||||
async testNodeOperations() {
|
||||
console.log('Testing Node Operations...');
|
||||
|
||||
// Create a test node first
|
||||
const createResult = await this.runTest('scene', 'create-node', {
|
||||
name: 'TestNodeForOps',
|
||||
type: 'cc.Node'
|
||||
});
|
||||
|
||||
if (createResult.success && createResult.result) {
|
||||
const nodeUuid = createResult.result;
|
||||
|
||||
// Test set property
|
||||
await this.runTest('scene', 'set-property', {
|
||||
uuid: nodeUuid,
|
||||
path: 'position',
|
||||
dump: {
|
||||
type: 'cc.Vec3',
|
||||
value: { x: 100, y: 200, z: 0 }
|
||||
}
|
||||
});
|
||||
|
||||
// Test add component
|
||||
await this.runTest('scene', 'add-component', {
|
||||
uuid: nodeUuid,
|
||||
component: 'cc.Sprite'
|
||||
});
|
||||
|
||||
// Clean up
|
||||
await this.runTest('scene', 'remove-node', nodeUuid);
|
||||
}
|
||||
}
|
||||
|
||||
async testAssetOperations() {
|
||||
console.log('Testing Asset Operations...');
|
||||
|
||||
// Test asset list
|
||||
await this.runTest('asset-db', 'query-assets', {
|
||||
pattern: '**/*.png',
|
||||
ccType: 'cc.ImageAsset'
|
||||
});
|
||||
|
||||
// Test query asset by path
|
||||
await this.runTest('asset-db', 'query-path', 'db://assets');
|
||||
|
||||
// Test query asset by uuid (using a valid uuid format)
|
||||
await this.runTest('asset-db', 'query-uuid', 'db://assets');
|
||||
}
|
||||
|
||||
async testProjectOperations() {
|
||||
console.log('Testing Project Operations...');
|
||||
|
||||
// Test open project settings
|
||||
await this.runTest('project', 'open-settings', {});
|
||||
|
||||
// Test query project settings
|
||||
const projectName = await this.runTest('project', 'query-setting', 'name');
|
||||
|
||||
if (projectName.success) {
|
||||
console.log('Project name:', projectName.result);
|
||||
}
|
||||
}
|
||||
|
||||
async runAllTests() {
|
||||
this.results = [];
|
||||
|
||||
await this.testSceneOperations();
|
||||
await this.testNodeOperations();
|
||||
await this.testAssetOperations();
|
||||
await this.testProjectOperations();
|
||||
|
||||
return this.getTestReport();
|
||||
}
|
||||
|
||||
getTestReport() {
|
||||
const total = this.results.length;
|
||||
const passed = this.results.filter(r => r.success).length;
|
||||
const failed = total - passed;
|
||||
|
||||
return {
|
||||
summary: {
|
||||
total,
|
||||
passed,
|
||||
failed,
|
||||
passRate: total > 0 ? (passed / total * 100).toFixed(2) + '%' : '0%'
|
||||
},
|
||||
results: this.results,
|
||||
grouped: this.groupResultsByTool()
|
||||
};
|
||||
}
|
||||
|
||||
private groupResultsByTool() {
|
||||
const grouped: Record<string, TestResult[]> = {};
|
||||
|
||||
for (const result of this.results) {
|
||||
if (!grouped[result.tool]) {
|
||||
grouped[result.tool] = [];
|
||||
}
|
||||
grouped[result.tool].push(result);
|
||||
}
|
||||
|
||||
return grouped;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user