优化了组件属性的设置的接口,使AI调用起来更简单,更准确。每次更新场景信息、组件信息或者节点信息,端口将会返回最新的信息供AI进行追踪和比对。
This commit is contained in:
@@ -15,6 +15,7 @@ import { SceneAdvancedTools } from './tools/scene-advanced-tools';
|
||||
import { SceneViewTools } from './tools/scene-view-tools';
|
||||
import { ReferenceImageTools } from './tools/reference-image-tools';
|
||||
import { AssetAdvancedTools } from './tools/asset-advanced-tools';
|
||||
import { ValidationTools } from './tools/validation-tools';
|
||||
|
||||
export class MCPServer {
|
||||
private settings: MCPServerSettings;
|
||||
@@ -44,6 +45,7 @@ export class MCPServer {
|
||||
this.tools.sceneView = new SceneViewTools();
|
||||
this.tools.referenceImage = new ReferenceImageTools();
|
||||
this.tools.assetAdvanced = new AssetAdvancedTools();
|
||||
this.tools.validation = new ValidationTools();
|
||||
console.log('[MCPServer] Tools initialized successfully');
|
||||
} catch (error) {
|
||||
console.error('[MCPServer] Error initializing tools:', error);
|
||||
@@ -146,6 +148,11 @@ export class MCPServer {
|
||||
} else if (pathname === '/health' && req.method === 'GET') {
|
||||
res.writeHead(200);
|
||||
res.end(JSON.stringify({ status: 'ok', tools: this.toolsList.length }));
|
||||
} else if (pathname?.startsWith('/api/') && req.method === 'POST') {
|
||||
await this.handleSimpleAPIRequest(req, res, pathname);
|
||||
} else if (pathname === '/api/tools' && req.method === 'GET') {
|
||||
res.writeHead(200);
|
||||
res.end(JSON.stringify({ tools: this.getSimplifiedToolsList() }));
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end(JSON.stringify({ error: 'Not found' }));
|
||||
@@ -166,11 +173,25 @@ export class MCPServer {
|
||||
|
||||
req.on('end', async () => {
|
||||
try {
|
||||
const message = JSON.parse(body);
|
||||
// Enhanced JSON parsing with better error handling
|
||||
let message;
|
||||
try {
|
||||
message = JSON.parse(body);
|
||||
} catch (parseError: any) {
|
||||
// Try to fix common JSON issues
|
||||
const fixedBody = this.fixCommonJsonIssues(body);
|
||||
try {
|
||||
message = JSON.parse(fixedBody);
|
||||
console.log('[MCPServer] Fixed JSON parsing issue');
|
||||
} catch (secondError) {
|
||||
throw new Error(`JSON parsing failed: ${parseError.message}. Original body: ${body.substring(0, 500)}...`);
|
||||
}
|
||||
}
|
||||
|
||||
const response = await this.handleMessage(message);
|
||||
res.writeHead(200);
|
||||
res.end(JSON.stringify(response));
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error('Error handling MCP request:', error);
|
||||
res.writeHead(400);
|
||||
res.end(JSON.stringify({
|
||||
@@ -178,7 +199,7 @@ export class MCPServer {
|
||||
id: null,
|
||||
error: {
|
||||
code: -32700,
|
||||
message: 'Parse error'
|
||||
message: `Parse error: ${error.message}`
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -234,6 +255,27 @@ export class MCPServer {
|
||||
}
|
||||
}
|
||||
|
||||
private fixCommonJsonIssues(jsonStr: string): string {
|
||||
let fixed = jsonStr;
|
||||
|
||||
// Fix common escape character issues
|
||||
fixed = fixed
|
||||
// Fix unescaped quotes in strings
|
||||
.replace(/([^\\])"([^"]*[^\\])"([^,}\]:])/g, '$1\\"$2\\"$3')
|
||||
// Fix unescaped backslashes
|
||||
.replace(/([^\\])\\([^"\\\/bfnrt])/g, '$1\\\\$2')
|
||||
// Fix trailing commas
|
||||
.replace(/,(\s*[}\]])/g, '$1')
|
||||
// Fix single quotes (should be double quotes)
|
||||
.replace(/'/g, '"')
|
||||
// Fix common control characters
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\t/g, '\\t');
|
||||
|
||||
return fixed;
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
if (this.httpServer) {
|
||||
this.httpServer.close();
|
||||
@@ -252,6 +294,123 @@ export class MCPServer {
|
||||
};
|
||||
}
|
||||
|
||||
private async handleSimpleAPIRequest(req: http.IncomingMessage, res: http.ServerResponse, pathname: string): Promise<void> {
|
||||
let body = '';
|
||||
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk.toString();
|
||||
});
|
||||
|
||||
req.on('end', async () => {
|
||||
try {
|
||||
// Extract tool name from path like /api/node/set_position
|
||||
const pathParts = pathname.split('/').filter(p => p);
|
||||
if (pathParts.length < 3) {
|
||||
res.writeHead(400);
|
||||
res.end(JSON.stringify({ error: 'Invalid API path. Use /api/{category}/{tool_name}' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const category = pathParts[1];
|
||||
const toolName = pathParts[2];
|
||||
const fullToolName = `${category}_${toolName}`;
|
||||
|
||||
// Parse parameters with enhanced error handling
|
||||
let params;
|
||||
try {
|
||||
params = body ? JSON.parse(body) : {};
|
||||
} catch (parseError: any) {
|
||||
// Try to fix JSON issues
|
||||
const fixedBody = this.fixCommonJsonIssues(body);
|
||||
try {
|
||||
params = JSON.parse(fixedBody);
|
||||
console.log('[MCPServer] Fixed API JSON parsing issue');
|
||||
} catch (secondError: any) {
|
||||
res.writeHead(400);
|
||||
res.end(JSON.stringify({
|
||||
error: 'Invalid JSON in request body',
|
||||
details: parseError.message,
|
||||
receivedBody: body.substring(0, 200)
|
||||
}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute tool
|
||||
const result = await this.executeToolCall(fullToolName, params);
|
||||
|
||||
res.writeHead(200);
|
||||
res.end(JSON.stringify({
|
||||
success: true,
|
||||
tool: fullToolName,
|
||||
result: result
|
||||
}));
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('Simple API error:', error);
|
||||
res.writeHead(500);
|
||||
res.end(JSON.stringify({
|
||||
success: false,
|
||||
error: error.message,
|
||||
tool: pathname
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getSimplifiedToolsList(): any[] {
|
||||
return this.toolsList.map(tool => {
|
||||
const parts = tool.name.split('_');
|
||||
const category = parts[0];
|
||||
const toolName = parts.slice(1).join('_');
|
||||
|
||||
return {
|
||||
name: tool.name,
|
||||
category: category,
|
||||
toolName: toolName,
|
||||
description: tool.description,
|
||||
apiPath: `/api/${category}/${toolName}`,
|
||||
curlExample: this.generateCurlExample(category, toolName, tool.inputSchema)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private generateCurlExample(category: string, toolName: string, schema: any): string {
|
||||
// Generate sample parameters based on schema
|
||||
const sampleParams = this.generateSampleParams(schema);
|
||||
const jsonString = JSON.stringify(sampleParams, null, 2);
|
||||
|
||||
return `curl -X POST http://127.0.0.1:8585/api/${category}/${toolName} \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '${jsonString}'`;
|
||||
}
|
||||
|
||||
private generateSampleParams(schema: any): any {
|
||||
if (!schema || !schema.properties) return {};
|
||||
|
||||
const sample: any = {};
|
||||
for (const [key, prop] of Object.entries(schema.properties as any)) {
|
||||
const propSchema = prop as any;
|
||||
switch (propSchema.type) {
|
||||
case 'string':
|
||||
sample[key] = propSchema.default || 'example_string';
|
||||
break;
|
||||
case 'number':
|
||||
sample[key] = propSchema.default || 42;
|
||||
break;
|
||||
case 'boolean':
|
||||
sample[key] = propSchema.default || true;
|
||||
break;
|
||||
case 'object':
|
||||
sample[key] = propSchema.default || { x: 0, y: 0, z: 0 };
|
||||
break;
|
||||
default:
|
||||
sample[key] = 'example_value';
|
||||
}
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
public updateSettings(settings: MCPServerSettings) {
|
||||
this.settings = settings;
|
||||
if (this.httpServer) {
|
||||
|
||||
Reference in New Issue
Block a user